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