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