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