UIQ3 bugfixes, SVP drc indirect jumps, stuff
[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"
7836e87c 13#include "lprintf.h"
14
15static char *mystrip(char *str);
16
17#ifndef _MSC_VER
18
c51bd2fe 19#include "menu.h"
20#include "emu.h"
21#include <Pico/Pico.h>
22
23extern menu_entry opt_entries[];
24extern menu_entry opt2_entries[];
25extern menu_entry cdopt_entries[];
6589c840 26extern menu_entry ctrlopt_entries[];
c51bd2fe 27extern const int opt_entry_count;
28extern const int opt2_entry_count;
29extern const int cdopt_entry_count;
6589c840 30extern const int ctrlopt_entry_count;
960a8e27 31#ifdef PSP
32extern menu_entry opt3_entries[];
33extern const int opt3_entry_count;
34#endif
c51bd2fe 35
960a8e27 36static menu_entry *cfg_opts[] =
37{
38 opt_entries,
39 opt2_entries,
40 cdopt_entries,
6589c840 41 ctrlopt_entries,
960a8e27 42#ifdef PSP
43 opt3_entries,
44#endif
45};
46
47static const int *cfg_opt_counts[] =
48{
49 &opt_entry_count,
50 &opt2_entry_count,
51 &cdopt_entry_count,
6589c840 52 &ctrlopt_entry_count,
960a8e27 53#ifdef PSP
54 &opt3_entry_count,
55#endif
56};
c51bd2fe 57
67dfdf5f 58#define NL "\r\n"
c51bd2fe 59
60
61static int seek_sect(FILE *f, const char *section)
62{
63 char line[128], *tmp;
64 int len;
65
66 len = strlen(section);
67 // seek to the section needed
68 while (!feof(f))
69 {
70 tmp = fgets(line, sizeof(line), f);
71 if (tmp == NULL) break;
72
73 if (line[0] != '[') continue; // not section start
74 if (strncmp(line + 1, section, len) == 0 && line[len+1] == ']')
75 return 1; // found it
76 }
77
78 return 0;
79}
80
81
3e85ebdd 82static void custom_write(FILE *f, const menu_entry *me, int no_def)
c51bd2fe 83{
84 char *str, str24[24];
85
86 switch (me->id)
87 {
88 case MA_OPT_RENDERER:
dd5fd477 89 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_ALT_RENDERER) &&
c51bd2fe 90 !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x80)) return;
dd5fd477 91 if (PicoOpt&POPT_ALT_RENDERER)
960a8e27 92 str =
93#ifndef PSP
94 "8bit "
95#endif
96 "fast";
c51bd2fe 97 else if (currentConfig.EmuOpt&0x80)
960a8e27 98 str =
99#ifndef PSP
100 "16bit "
101#endif
102 "accurate";
c51bd2fe 103 else
104 str = "8bit accurate";
105 fprintf(f, "Renderer = %s", str);
106 break;
107
108 case MA_OPT_SCALING:
3e85ebdd 109 if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
960a8e27 110#ifdef __GP2X__
c51bd2fe 111 switch (currentConfig.scaling) {
112 default: str = "OFF"; break;
113 case 1: str = "hw horizontal"; break;
114 case 2: str = "hw horiz. + vert."; break;
115 case 3: str = "sw horizontal"; break;
116 }
117 fprintf(f, "Scaling = %s", str);
960a8e27 118#endif
c51bd2fe 119 break;
120 case MA_OPT_FRAMESKIP:
3e85ebdd 121 if (no_def && defaultConfig.Frameskip == currentConfig.Frameskip) return;
c51bd2fe 122 if (currentConfig.Frameskip < 0)
123 strcpy(str24, "Auto");
124 else sprintf(str24, "%i", currentConfig.Frameskip);
125 fprintf(f, "Frameskip = %s", str24);
126 break;
127 case MA_OPT_SOUND_QUALITY:
dd5fd477 128 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_EN_STEREO) &&
c51bd2fe 129 defaultConfig.s_PsndRate == PsndRate) return;
dd5fd477 130 str = (PicoOpt&POPT_EN_STEREO)?"stereo":"mono";
c51bd2fe 131 fprintf(f, "Sound Quality = %i %s", PsndRate, str);
132 break;
133 case MA_OPT_REGION:
3e85ebdd 134 if (no_def && defaultConfig.s_PicoRegion == PicoRegionOverride &&
c51bd2fe 135 defaultConfig.s_PicoAutoRgnOrder == PicoAutoRgnOrder) return;
960a8e27 136 strncpy(str24, me_region_name(PicoRegionOverride, PicoAutoRgnOrder), 23); str24[23] = 0;
137 fprintf(f, "Region = %s", mystrip(str24));
c51bd2fe 138 break;
139 case MA_OPT_CONFIRM_STATES:
3e85ebdd 140 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&(5<<9))) return;
c51bd2fe 141 switch ((currentConfig.EmuOpt >> 9) & 5) {
142 default: str = "OFF"; break;
143 case 1: str = "writes"; break;
144 case 4: str = "loads"; break;
145 case 5: str = "both"; break;
146 }
147 fprintf(f, "Confirm savestate = %s", str);
148 break;
149 case MA_OPT_CPU_CLOCKS:
3e85ebdd 150 if (no_def && defaultConfig.CPUclock == currentConfig.CPUclock) return;
960a8e27 151#ifdef __GP2X__
c51bd2fe 152 fprintf(f, "GP2X CPU clocks = %i", currentConfig.CPUclock);
960a8e27 153#elif defined(PSP)
154 fprintf(f, "PSP CPU clock = %i", currentConfig.CPUclock);
155#endif
c51bd2fe 156 break;
157 case MA_OPT2_GAMMA:
3e85ebdd 158 if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
c51bd2fe 159 fprintf(f, "Gamma correction = %.3f", (double)currentConfig.gamma / 100.0);
160 break;
161 case MA_OPT2_SQUIDGEHACK:
3e85ebdd 162 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x0010)) return;
c51bd2fe 163 fprintf(f, "Squidgehack = %i", (currentConfig.EmuOpt&0x0010)>>4);
164 break;
165 case MA_CDOPT_READAHEAD:
3e85ebdd 166 if (no_def && defaultConfig.s_PicoCDBuffers == PicoCDBuffers) return;
c51bd2fe 167 sprintf(str24, "%i", PicoCDBuffers * 2);
168 fprintf(f, "ReadAhead buffer = %s", str24);
169 break;
960a8e27 170 /* PSP */
171 case MA_OPT3_SCALE:
172 if (no_def && defaultConfig.scale == currentConfig.scale) return;
173 fprintf(f, "Scale factor = %.2f", currentConfig.scale);
174 break;
175 case MA_OPT3_HSCALE32:
176 if (no_def && defaultConfig.hscale32 == currentConfig.hscale32) return;
177 fprintf(f, "Hor. scale (for low res. games) = %.2f", currentConfig.hscale32);
178 break;
179 case MA_OPT3_HSCALE40:
180 if (no_def && defaultConfig.hscale40 == currentConfig.hscale40) return;
181 fprintf(f, "Hor. scale (for hi res. games) = %.2f", currentConfig.hscale40);
182 break;
183 case MA_OPT3_FILTERING:
184 if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
185 fprintf(f, "Bilinear filtering = %i", currentConfig.scaling);
186 break;
187 case MA_OPT3_GAMMAA:
188 if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
189 fprintf(f, "Gamma adjustment = %i", currentConfig.gamma);
190 break;
191 case MA_OPT3_BLACKLVL:
192 if (no_def && defaultConfig.gamma2 == currentConfig.gamma2) return;
193 fprintf(f, "Black level = %i", currentConfig.gamma2);
194 break;
195 case MA_OPT3_VSYNC:
196 if (no_def && (defaultConfig.EmuOpt&0x12000) == (currentConfig.gamma2&0x12000)) return;
197 strcpy(str24, "never");
198 if (currentConfig.EmuOpt & 0x2000)
199 strcpy(str24, (currentConfig.EmuOpt & 0x10000) ? "sometimes" : "always");
200 fprintf(f, "Wait for vsync = %s", str24);
201 break;
c51bd2fe 202
203 default:
3e85ebdd 204 lprintf("unhandled custom_write: %i\n", me->id);
c51bd2fe 205 return;
206 }
207 fprintf(f, NL);
208}
209
367b6f1f 210
36f6fd5a 211#if PLAT_HAVE_JOY
367b6f1f 212static const char *joyKeyNames[32] =
213{
214 "UP", "DOWN", "LEFT", "RIGHT", "b1", "b2", "b3", "b4",
215 "b5", "b6", "b7", "b8", "b9", "b10", "b11", "b12",
216 "b13", "b14", "b15", "b16", "b17", "b19", "b19", "b20",
217 "b21", "b22", "b23", "b24", "b25", "b26", "b27", "b28"
218};
36f6fd5a 219#endif
367b6f1f 220
221static void keys_write(FILE *fn, const char *bind_str, const int binds[32],
36f6fd5a 222 const int def_binds[32], const char * const names[32], int key_count, int no_defaults)
367b6f1f 223{
224 int t, i;
36f6fd5a 225 char act[48], name[32];
367b6f1f 226
36f6fd5a 227 for (t = 0; t < key_count; t++)
367b6f1f 228 {
229 act[0] = act[31] = 0;
230 if (no_defaults && binds[t] == def_binds[t])
231 continue;
367b6f1f 232#ifdef __GP2X__
233 if (strcmp(names[t], "SELECT") == 0) continue;
234#endif
36f6fd5a 235 if (t >= 32 || names[t] == NULL || strcmp(names[t], "???") == 0) {
236 if ((t >= '0' && t <= '9') || (t >= 'a' && t <= 'z') || (t >= 'A' && t <= 'Z'))
237 sprintf(name, "%c", t);
238 else
239 sprintf(name, "\\x%02x", t);
240 }
241 else
242 strcpy(name, names[t]);
243
960a8e27 244 if (binds[t] == 0 && def_binds[t] != 0) {
36f6fd5a 245 fprintf(fn, "%s %s =" NL, bind_str, name); // no binds
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
400 // save key config
36f6fd5a 401 keys_write(fn, "bind", currentConfig.KeyBinds, defaultConfig.KeyBinds, keyNames, PLAT_MAX_KEYS, no_defaults);
402#if PLAT_HAVE_JOY
403 keys_write(fn, "bind_joy0", currentConfig.JoyBinds[0], defaultConfig.JoyBinds[0], joyKeyNames, 32, 1);
404 keys_write(fn, "bind_joy1", currentConfig.JoyBinds[1], defaultConfig.JoyBinds[1], joyKeyNames, 32, 1);
405 keys_write(fn, "bind_joy2", currentConfig.JoyBinds[2], defaultConfig.JoyBinds[2], joyKeyNames, 32, 1);
406 keys_write(fn, "bind_joy3", currentConfig.JoyBinds[3], defaultConfig.JoyBinds[3], joyKeyNames, 32, 1);
407#endif
367b6f1f 408
960a8e27 409#ifndef PSP
67dfdf5f 410 if (section == NULL)
411 fprintf(fn, "Sound Volume = %i" NL, currentConfig.volume);
960a8e27 412#endif
67dfdf5f 413
c51bd2fe 414 fprintf(fn, NL);
415
416 if (fo != NULL)
417 {
418 // copy whatever is left
419 while (!feof(fo))
420 {
421 tmp = fgets(line, sizeof(line), fo);
422 if (tmp == NULL) break;
423
424 fputs(line, fn);
425 }
426 fclose(fo);
427 remove("tmp.cfg");
428 }
429
430 fclose(fn);
431 return 0;
432}
433
434
c51bd2fe 435int config_writelrom(const char *fname)
436{
437 char line[128], *tmp, *optr = NULL;
438 char *old_data = NULL;
439 int size;
440 FILE *f;
441
36f6fd5a 442 if (strlen(loadedRomFName) == 0) return -1;
c51bd2fe 443
444 f = fopen(fname, "r");
445 if (f != NULL)
446 {
447 fseek(f, 0, SEEK_END);
448 size = ftell(f);
449 fseek(f, 0, SEEK_SET);
450 old_data = malloc(size + size/8);
451 if (old_data != NULL)
452 {
453 optr = old_data;
454 while (!feof(f))
455 {
456 tmp = fgets(line, sizeof(line), f);
457 if (tmp == NULL) break;
458 mystrip(line);
459 if (strncasecmp(line, "LastUsedROM", 11) == 0)
460 continue;
461 sprintf(optr, "%s", line);
462 optr += strlen(optr);
463 }
464 }
465 fclose(f);
466 }
467
468 f = fopen(fname, "w");
469 if (f == NULL) return -1;
470
471 if (old_data != NULL) {
472 fwrite(old_data, 1, optr - old_data, f);
473 free(old_data);
474 }
36f6fd5a 475 fprintf(f, "LastUsedROM = %s" NL, loadedRomFName);
c51bd2fe 476 fclose(f);
477 return 0;
478}
479
367b6f1f 480/* --------------------------------------------------------------------------*/
481
482int config_readlrom(const char *fname)
483{
484 char line[128], *tmp;
485 int i, len, ret = -1;
486 FILE *f;
487
488 f = fopen(fname, "r");
489 if (f == NULL) return -1;
490
491 // seek to the section needed
492 while (!feof(f))
493 {
494 tmp = fgets(line, sizeof(line), f);
495 if (tmp == NULL) break;
496
497 if (strncasecmp(line, "LastUsedROM", 11) != 0) continue;
498 len = strlen(line);
499 for (i = 0; i < len; i++)
500 if (line[i] == '#' || line[i] == '\r' || line[i] == '\n') { line[i] = 0; break; }
501 tmp = strchr(line, '=');
502 if (tmp == NULL) break;
503 tmp++;
504 mystrip(tmp);
505
36f6fd5a 506 len = sizeof(loadedRomFName);
507 strncpy(loadedRomFName, tmp, len);
508 loadedRomFName[len-1] = 0;
367b6f1f 509 ret = 0;
510 break;
511 }
512 fclose(f);
513 return ret;
514}
515
c51bd2fe 516
517static int custom_read(menu_entry *me, const char *var, const char *val)
518{
519 char *tmp;
520 int tmpi;
521
522 switch (me->id)
523 {
524 case MA_OPT_RENDERER:
525 if (strcasecmp(var, "Renderer") != 0) return 0;
960a8e27 526 if (strcasecmp(val, "8bit fast") == 0 || strcasecmp(val, "fast") == 0) {
dd5fd477 527 PicoOpt |= POPT_ALT_RENDERER;
c51bd2fe 528 }
960a8e27 529 else if (strcasecmp(val, "16bit accurate") == 0 || strcasecmp(val, "accurate") == 0) {
dd5fd477 530 PicoOpt &= ~POPT_ALT_RENDERER;
c51bd2fe 531 currentConfig.EmuOpt |= 0x80;
532 }
533 else if (strcasecmp(val, "8bit accurate") == 0) {
dd5fd477 534 PicoOpt &= ~POPT_ALT_RENDERER;
c51bd2fe 535 currentConfig.EmuOpt &= ~0x80;
536 }
537 else
538 return 0;
539 return 1;
540
541 case MA_OPT_SCALING:
960a8e27 542#ifdef __GP2X__
c51bd2fe 543 if (strcasecmp(var, "Scaling") != 0) return 0;
544 if (strcasecmp(val, "OFF") == 0) {
545 currentConfig.scaling = 0;
546 } else if (strcasecmp(val, "hw horizontal") == 0) {
547 currentConfig.scaling = 1;
548 } else if (strcasecmp(val, "hw horiz. + vert.") == 0) {
549 currentConfig.scaling = 2;
550 } else if (strcasecmp(val, "sw horizontal") == 0) {
551 currentConfig.scaling = 3;
552 } else
553 return 0;
554 return 1;
960a8e27 555#else
556 return 0;
557#endif
c51bd2fe 558
559 case MA_OPT_FRAMESKIP:
560 if (strcasecmp(var, "Frameskip") != 0) return 0;
561 if (strcasecmp(val, "Auto") == 0)
562 currentConfig.Frameskip = -1;
563 else currentConfig.Frameskip = atoi(val);
564 return 1;
565
566 case MA_OPT_SOUND_QUALITY:
567 if (strcasecmp(var, "Sound Quality") != 0) return 0;
568 PsndRate = strtoul(val, &tmp, 10);
569 if (PsndRate < 8000 || PsndRate > 44100)
570 PsndRate = 22050;
571 while (*tmp == ' ') tmp++;
572 if (strcasecmp(tmp, "stereo") == 0) {
dd5fd477 573 PicoOpt |= POPT_EN_STEREO;
c51bd2fe 574 } else if (strcasecmp(tmp, "mono") == 0) {
dd5fd477 575 PicoOpt &= ~POPT_EN_STEREO;
c51bd2fe 576 } else
577 return 0;
578 return 1;
579
580 case MA_OPT_REGION:
581 if (strcasecmp(var, "Region") != 0) return 0;
582 if (strncasecmp(val, "Auto: ", 6) == 0)
583 {
584 const char *p = val + 5, *end = val + strlen(val);
585 int i;
586 PicoRegionOverride = PicoAutoRgnOrder = 0;
3e85ebdd 587 for (i = 0; p < end && i < 3; i++)
588 {
589 while (*p == ' ') p++;
c51bd2fe 590 if (p[0] == 'J' && p[1] == 'P') {
591 PicoAutoRgnOrder |= 1 << (i*4);
592 } else if (p[0] == 'U' && p[1] == 'S') {
593 PicoAutoRgnOrder |= 4 << (i*4);
594 } else if (p[0] == 'E' && p[1] == 'U') {
595 PicoAutoRgnOrder |= 8 << (i*4);
596 }
3e85ebdd 597 while (*p != ' ' && *p != 0) p++;
598 if (*p == 0) break;
c51bd2fe 599 }
600 }
601 else if (strcasecmp(val, "Auto") == 0) {
602 PicoRegionOverride = 0;
603 } else if (strcasecmp(val, "Japan NTSC") == 0) {
604 PicoRegionOverride = 1;
605 } else if (strcasecmp(val, "Japan PAL") == 0) {
606 PicoRegionOverride = 2;
607 } else if (strcasecmp(val, "USA") == 0) {
608 PicoRegionOverride = 4;
609 } else if (strcasecmp(val, "Europe") == 0) {
610 PicoRegionOverride = 8;
611 } else
612 return 0;
613 return 1;
614
615 case MA_OPT_CONFIRM_STATES:
616 if (strcasecmp(var, "Confirm savestate") != 0) return 0;
617 if (strcasecmp(val, "OFF") == 0) {
367b6f1f 618 currentConfig.EmuOpt &= ~(5<<9);
c51bd2fe 619 } else if (strcasecmp(val, "writes") == 0) {
367b6f1f 620 currentConfig.EmuOpt &= ~(5<<9);
621 currentConfig.EmuOpt |= 1<<9;
c51bd2fe 622 } else if (strcasecmp(val, "loads") == 0) {
367b6f1f 623 currentConfig.EmuOpt &= ~(5<<9);
624 currentConfig.EmuOpt |= 4<<9;
c51bd2fe 625 } else if (strcasecmp(val, "both") == 0) {
367b6f1f 626 currentConfig.EmuOpt &= ~(5<<9);
627 currentConfig.EmuOpt |= 5<<9;
c51bd2fe 628 } else
629 return 0;
630 return 1;
631
632 case MA_OPT_CPU_CLOCKS:
960a8e27 633#ifdef __GP2X__
c51bd2fe 634 if (strcasecmp(var, "GP2X CPU clocks") != 0) return 0;
960a8e27 635#elif defined(PSP)
636 if (strcasecmp(var, "PSP CPU clock") != 0) return 0;
637#endif
c51bd2fe 638 currentConfig.CPUclock = atoi(val);
639 return 1;
640
641 case MA_OPT2_GAMMA:
642 if (strcasecmp(var, "Gamma correction") != 0) return 0;
643 currentConfig.gamma = (int) (atof(val) * 100.0);
644 return 1;
645
646 case MA_OPT2_SQUIDGEHACK:
647 if (strcasecmp(var, "Squidgehack") != 0) return 0;
648 tmpi = atoi(val);
649 if (tmpi) *(int *)me->var |= me->mask;
650 else *(int *)me->var &= ~me->mask;
651 return 1;
652
653 case MA_CDOPT_READAHEAD:
654 if (strcasecmp(var, "ReadAhead buffer") != 0) return 0;
655 PicoCDBuffers = atoi(val) / 2;
656 return 1;
657
960a8e27 658 /* PSP */
659 case MA_OPT3_SCALE:
660 if (strcasecmp(var, "Scale factor") != 0) return 0;
661 currentConfig.scale = atof(val);
662 return 1;
663 case MA_OPT3_HSCALE32:
664 if (strcasecmp(var, "Hor. scale (for low res. games)") != 0) return 0;
665 currentConfig.hscale32 = atof(val);
666 return 1;
667 case MA_OPT3_HSCALE40:
668 if (strcasecmp(var, "Hor. scale (for hi res. games)") != 0) return 0;
669 currentConfig.hscale40 = atof(val);
670 return 1;
671 case MA_OPT3_FILTERING:
672 if (strcasecmp(var, "Bilinear filtering") != 0) return 0;
673 currentConfig.scaling = atoi(val);
674 return 1;
675 case MA_OPT3_GAMMAA:
676 if (strcasecmp(var, "Gamma adjustment") != 0) return 0;
677 currentConfig.gamma = atoi(val);
678 return 1;
679 case MA_OPT3_BLACKLVL:
680 if (strcasecmp(var, "Black level") != 0) return 0;
681 currentConfig.gamma2 = atoi(val);
682 return 1;
683 case MA_OPT3_VSYNC:
684 if (strcasecmp(var, "Wait for vsync") != 0) return 0;
685 if (strcasecmp(val, "never") == 0) {
686 currentConfig.EmuOpt &= ~0x12000;
687 } else if (strcasecmp(val, "sometimes") == 0) {
688 currentConfig.EmuOpt |= 0x12000;
689 } else if (strcasecmp(val, "always") == 0) {
690 currentConfig.EmuOpt &= ~0x12000;
691 currentConfig.EmuOpt |= 0x02000;
692 } else
693 return 0;
694 return 1;
695
c51bd2fe 696 default:
3e85ebdd 697 lprintf("unhandled custom_read: %i\n", me->id);
c51bd2fe 698 return 0;
699 }
700}
701
702
8e708f92 703static unsigned int keys_encountered = 0;
704
36f6fd5a 705static void keys_parse(const char *var, const char *val, int binds[32],
706 const char * const names[32], int max_keys)
367b6f1f 707{
8e708f92 708 int t, i;
367b6f1f 709 unsigned int player;
710
711 for (t = 0; t < 32; t++)
712 {
36f6fd5a 713 if (names[t] && strcmp(names[t], var) == 0) break;
367b6f1f 714 }
36f6fd5a 715 if (t == 32)
716 {
717 int len = strlen(var);
718 if (len == 1) t = var[0];
719 else if (len >= 4 && var[0] == '\\' && var[1] == 'x') {
720 char *p;
721 t = (int)strtoul(var + 2, &p, 16);
722 if (*p != 0) t = max_keys; // parse failed
723 }
724 else
725 t = max_keys; // invalid
726 }
727 if (t < 0 || t >= max_keys) {
367b6f1f 728 lprintf("unhandled bind \"%s\"\n", var);
729 return;
730 }
731
8e708f92 732 if (binds == currentConfig.KeyBinds && !(keys_encountered & (1<<t))) { // hack
367b6f1f 733 binds[t] = 0;
734 keys_encountered |= 1<<t;
735 }
736 if (val[0] == 0)
737 return;
738 if (strncasecmp(val, "player", 6) == 0)
739 {
740 player = atoi(val + 6) - 1;
741 if (player > 1) goto fail;
742 for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) {
743 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0) {
744 binds[t] |= me_ctrl_actions[i].mask | (player<<16);
745 return;
746 }
747 }
748 }
749 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
750 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0) {
751 binds[t] |= emuctrl_actions[i].mask;
752 return;
753 }
754 }
755
756fail:
757 lprintf("unhandled action \"%s\"\n", val);
758 return;
367b6f1f 759}
760
761
762#define try_joy_parse(num) { \
763 if (strncasecmp(var, "bind_joy"#num " ", 10) == 0) { \
36f6fd5a 764 keys_parse(var + 10, val, currentConfig.JoyBinds[num], joyKeyNames, 32); \
367b6f1f 765 return; \
766 } \
767}
768
c51bd2fe 769static void parse(const char *var, const char *val)
770{
771 menu_entry *me;
772 int t, i, tlen, tmp, ret = 0;
773
367b6f1f 774 if (strcasecmp(var, "LastUsedROM") == 0)
775 return; /* handled elsewhere */
776
67dfdf5f 777 if (strcasecmp(var, "Sound Volume") == 0) {
778 currentConfig.volume = atoi(val);
779 return;
780 }
781
367b6f1f 782 // key binds
783 if (strncasecmp(var, "bind ", 5) == 0) {
36f6fd5a 784 keys_parse(var + 5, val, currentConfig.KeyBinds, keyNames, PLAT_MAX_KEYS);
367b6f1f 785 return;
786 }
36f6fd5a 787#if PLAT_HAVE_JOY
367b6f1f 788 try_joy_parse(0)
789 try_joy_parse(1)
790 try_joy_parse(2)
791 try_joy_parse(3)
36f6fd5a 792#endif
367b6f1f 793
c51bd2fe 794 for (t = 0; t < sizeof(cfg_opts) / sizeof(cfg_opts[0]) && ret == 0; t++)
795 {
796 me = cfg_opts[t];
797 tlen = *(cfg_opt_counts[t]);
798 for (i = 0; i < tlen && ret == 0; i++, me++)
799 {
800 if (!me->need_to_save) continue;
801 if (me->name != NULL) {
802 if (strcasecmp(var, me->name) != 0) continue; // surely not this one
803 if (me->beh == MB_ONOFF) {
804 tmp = atoi(val);
805 if (tmp) *(int *)me->var |= me->mask;
806 else *(int *)me->var &= ~me->mask;
807 return;
808 } else if (me->beh == MB_RANGE) {
809 tmp = atoi(val);
810 if (tmp < me->min) tmp = me->min;
811 if (tmp > me->max) tmp = me->max;
812 *(int *)me->var = tmp;
813 return;
814 }
815 }
816 ret = custom_read(me, var, val);
817 }
818 }
5d239ae7 819 if (!ret) lprintf("config_readsect: unhandled var: \"%s\"\n", var);
c51bd2fe 820}
821
822
367b6f1f 823int config_havesect(const char *fname, const char *section)
824{
825 FILE *f;
826 int ret;
827
828 f = fopen(fname, "r");
829 if (f == NULL) return 0;
830
831 ret = seek_sect(f, section);
832 fclose(f);
833 return ret;
834}
835
c51bd2fe 836int config_readsect(const char *fname, const char *section)
837{
7836e87c 838 char line[128], *var, *val;
367b6f1f 839 FILE *f;
7836e87c 840 int ret;
c51bd2fe 841
367b6f1f 842 f = fopen(fname, "r");
a5365695 843 if (f == NULL) return -1;
c51bd2fe 844
845 if (section != NULL)
846 {
847 ret = seek_sect(f, section);
848 if (!ret) {
3e85ebdd 849 lprintf("config_readsect: %s: missing section [%s]\n", fname, section);
c51bd2fe 850 fclose(f);
851 return -1;
852 }
853 }
854
8e708f92 855 keys_encountered = 0;
856
c51bd2fe 857 while (!feof(f))
858 {
7836e87c 859 ret = config_get_var_val(f, line, sizeof(line), &var, &val);
860 if (ret == 0) break;
861 if (ret == -1) continue;
c51bd2fe 862
863 parse(var, val);
864 }
865
866 fclose(f);
867 return 0;
868}
869
7836e87c 870#endif // _MSC_VER
871
872static char *mystrip(char *str)
873{
874 int i, len;
875
876 len = strlen(str);
877 for (i = 0; i < len; i++)
878 if (str[i] != ' ') break;
879 if (i > 0) memmove(str, str + i, len - i + 1);
880
881 len = strlen(str);
882 for (i = len - 1; i >= 0; i--)
883 if (str[i] != ' ') break;
884 str[i+1] = 0;
885
886 return str;
887}
888
889/* returns:
890 * 0 - EOF, end
891 * 1 - parsed ok
892 * -1 - failed to parse line
893 */
894int config_get_var_val(void *file, char *line, int lsize, char **rvar, char **rval)
895{
896 char *var, *val, *tmp;
897 FILE *f = file;
898 int len, i;
899
900 tmp = fgets(line, lsize, f);
901 if (tmp == NULL) return 0;
902
903 if (line[0] == '[') return 0; // other section
904
905 // strip comments, linefeed, spaces..
906 len = strlen(line);
907 for (i = 0; i < len; i++)
908 if (line[i] == '#' || line[i] == '\r' || line[i] == '\n') { line[i] = 0; break; }
909 mystrip(line);
910 len = strlen(line);
911 if (len <= 0) return -1;;
912
913 // get var and val
914 for (i = 0; i < len; i++)
915 if (line[i] == '=') break;
916 if (i >= len || strchr(&line[i+1], '=') != NULL) {
917 lprintf("config_readsect: can't parse: %s\n", line);
918 return -1;
919 }
920 line[i] = 0;
921 var = line;
922 val = &line[i+1];
923 mystrip(var);
924 mystrip(val);
925
926#ifndef _MSC_VER
927 if (strlen(var) == 0 || (strlen(val) == 0 && strncasecmp(var, "bind", 4) != 0)) {
928 lprintf("config_readsect: something's empty: \"%s\" = \"%s\"\n", var, val);
929 return -1;;
930 }
931#endif
932
933 *rvar = var;
934 *rval = val;
935 return 1;
936}
937