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