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