2 * Human-readable config file management for PicoDrive
16 static char *mystrip(char *str);
22 #include <pico/pico.h>
24 extern menu_entry opt_entries[];
25 extern menu_entry opt2_entries[];
26 extern menu_entry cdopt_entries[];
27 extern menu_entry ctrlopt_entries[];
28 extern const int opt_entry_count;
29 extern const int opt2_entry_count;
30 extern const int cdopt_entry_count;
31 extern const int ctrlopt_entry_count;
33 extern menu_entry opt3_entries[];
34 extern const int opt3_entry_count;
37 static menu_entry *cfg_opts[] =
48 static const int *cfg_opt_counts[] =
62 static int seek_sect(FILE *f, const char *section)
67 len = strlen(section);
68 // seek to the section needed
71 tmp = fgets(line, sizeof(line), f);
72 if (tmp == NULL) break;
74 if (line[0] != '[') continue; // not section start
75 if (strncmp(line + 1, section, len) == 0 && line[len+1] == ']')
83 static void custom_write(FILE *f, const menu_entry *me, int no_def)
90 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_ALT_RENDERER) &&
91 !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x80)) return;
92 if (PicoOpt&POPT_ALT_RENDERER)
98 else if (currentConfig.EmuOpt&0x80)
105 str = "8bit accurate";
106 fprintf(f, "Renderer = %s", str);
110 if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
112 switch (currentConfig.scaling) {
113 default: str = "OFF"; break;
114 case 1: str = "hw horizontal"; break;
115 case 2: str = "hw horiz. + vert."; break;
116 case 3: str = "sw horizontal"; break;
118 fprintf(f, "Scaling = %s", str);
121 case MA_OPT_FRAMESKIP:
122 if (no_def && defaultConfig.Frameskip == currentConfig.Frameskip) return;
123 if (currentConfig.Frameskip < 0)
124 strcpy(str24, "Auto");
125 else sprintf(str24, "%i", currentConfig.Frameskip);
126 fprintf(f, "Frameskip = %s", str24);
128 case MA_OPT_SOUND_QUALITY:
129 if (no_def && !((defaultConfig.s_PicoOpt^PicoOpt)&POPT_EN_STEREO) &&
130 defaultConfig.s_PsndRate == PsndRate) return;
131 str = (PicoOpt&POPT_EN_STEREO)?"stereo":"mono";
132 fprintf(f, "Sound Quality = %i %s", PsndRate, str);
135 if (no_def && defaultConfig.s_PicoRegion == PicoRegionOverride &&
136 defaultConfig.s_PicoAutoRgnOrder == PicoAutoRgnOrder) return;
137 strncpy(str24, me_region_name(PicoRegionOverride, PicoAutoRgnOrder), 23); str24[23] = 0;
138 fprintf(f, "Region = %s", mystrip(str24));
140 case MA_OPT_CONFIRM_STATES:
141 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&(5<<9))) return;
142 switch ((currentConfig.EmuOpt >> 9) & 5) {
143 default: str = "OFF"; break;
144 case 1: str = "writes"; break;
145 case 4: str = "loads"; break;
146 case 5: str = "both"; break;
148 fprintf(f, "Confirm savestate = %s", str);
150 case MA_OPT_CPU_CLOCKS:
151 if (no_def && defaultConfig.CPUclock == currentConfig.CPUclock) return;
153 fprintf(f, "GP2X CPU clocks = %i", currentConfig.CPUclock);
155 fprintf(f, "PSP CPU clock = %i", currentConfig.CPUclock);
159 if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
160 fprintf(f, "Gamma correction = %.3f", (double)currentConfig.gamma / 100.0);
162 case MA_OPT2_SQUIDGEHACK:
163 if (no_def && !((defaultConfig.EmuOpt^currentConfig.EmuOpt)&0x0010)) return;
164 fprintf(f, "Squidgehack = %i", (currentConfig.EmuOpt&0x0010)>>4);
166 case MA_CDOPT_READAHEAD:
167 if (no_def && defaultConfig.s_PicoCDBuffers == PicoCDBuffers) return;
168 sprintf(str24, "%i", PicoCDBuffers * 2);
169 fprintf(f, "ReadAhead buffer = %s", str24);
173 if (no_def && defaultConfig.scale == currentConfig.scale) return;
174 fprintf(f, "Scale factor = %.2f", currentConfig.scale);
176 case MA_OPT3_HSCALE32:
177 if (no_def && defaultConfig.hscale32 == currentConfig.hscale32) return;
178 fprintf(f, "Hor. scale (for low res. games) = %.2f", currentConfig.hscale32);
180 case MA_OPT3_HSCALE40:
181 if (no_def && defaultConfig.hscale40 == currentConfig.hscale40) return;
182 fprintf(f, "Hor. scale (for hi res. games) = %.2f", currentConfig.hscale40);
184 case MA_OPT3_FILTERING:
185 if (no_def && defaultConfig.scaling == currentConfig.scaling) return;
186 fprintf(f, "Bilinear filtering = %i", currentConfig.scaling);
189 if (no_def && defaultConfig.gamma == currentConfig.gamma) return;
190 fprintf(f, "Gamma adjustment = %i", currentConfig.gamma);
192 case MA_OPT3_BLACKLVL:
193 if (no_def && defaultConfig.gamma2 == currentConfig.gamma2) return;
194 fprintf(f, "Black level = %i", currentConfig.gamma2);
197 if (no_def && (defaultConfig.EmuOpt&0x12000) == (currentConfig.gamma2&0x12000)) return;
198 strcpy(str24, "never");
199 if (currentConfig.EmuOpt & 0x2000)
200 strcpy(str24, (currentConfig.EmuOpt & 0x10000) ? "sometimes" : "always");
201 fprintf(f, "Wait for vsync = %s", str24);
205 lprintf("unhandled custom_write: %i\n", me->id);
213 static const char *joyKeyNames[32] =
215 "UP", "DOWN", "LEFT", "RIGHT", "b1", "b2", "b3", "b4",
216 "b5", "b6", "b7", "b8", "b9", "b10", "b11", "b12",
217 "b13", "b14", "b15", "b16", "b17", "b19", "b19", "b20",
218 "b21", "b22", "b23", "b24", "b25", "b26", "b27", "b28"
222 static void keys_write(FILE *fn, const char *bind_str, int dev_id, const int *binds, int no_defaults)
226 const int *def_binds;
228 key_count = in_get_dev_bind_count(dev_id);
229 def_binds = in_get_dev_def_binds(dev_id);
231 for (t = 0; t < key_count; t++)
234 act[0] = act[31] = 0;
236 if (no_defaults && binds[t] == def_binds[t])
239 name = in_get_key_name(dev_id, t);
241 if (strcmp(name, "SELECT") == 0) continue;
244 if (binds[t] == 0 && def_binds[t] != 0) {
245 fprintf(fn, "%s %s =" NL, bind_str, name);
249 for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) {
250 if (me_ctrl_actions[i].mask & binds[t]) {
251 strncpy(act, me_ctrl_actions[i].name, 31);
252 fprintf(fn, "%s %s = player%i %s" NL, bind_str, name,
253 ((binds[t]>>16)&1)+1, mystrip(act));
257 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
258 if (emuctrl_actions[i].mask & binds[t]) {
259 strncpy(act, emuctrl_actions[i].name, 31);
260 fprintf(fn, "%s %s = %s" NL, bind_str, name, mystrip(act));
267 static int default_var(const menu_entry *me)
271 case MA_OPT_ACC_TIMING:
272 case MA_OPT_ACC_SPRITES:
273 case MA_OPT_ARM940_SOUND:
274 case MA_OPT_6BUTTON_PAD:
275 case MA_OPT2_ENABLE_Z80:
276 case MA_OPT2_ENABLE_YM2612:
277 case MA_OPT2_ENABLE_SN76496:
278 case MA_OPT2_SVP_DYNAREC:
281 case MA_CDOPT_SAVERAM:
282 case MA_CDOPT_SCALEROT_CHIP:
283 case MA_CDOPT_BETTER_SYNC:
284 return defaultConfig.s_PicoOpt;
286 case MA_OPT_SHOW_FPS:
287 case MA_OPT_ENABLE_SOUND:
288 case MA_OPT_SRAM_STATES:
289 case MA_OPT2_A_SN_GAMMA:
291 case MA_OPT2_GZIP_STATES:
292 case MA_OPT2_NO_LAST_ROM:
293 case MA_OPT2_RAMTIMINGS:
295 return defaultConfig.EmuOpt;
297 case MA_CTRL_TURBO_RATE: return defaultConfig.turbo_rate;
298 case MA_OPT_SCALING: return defaultConfig.scaling;
299 case MA_OPT_ROTATION: return defaultConfig.rotation;
301 case MA_OPT_SAVE_SLOT:
307 int config_writesect(const char *fname, const char *section)
309 FILE *fo = NULL, *fn = NULL; // old and new
310 int no_defaults = 0; // avoid saving defaults
313 char line[128], *tmp;
319 fo = fopen(fname, "r");
321 fn = fopen(fname, "w");
325 ret = seek_sect(fo, section);
327 // sect not found, we can simply append
328 fclose(fo); fo = NULL;
329 fn = fopen(fname, "a");
335 rename(fname, "tmp.cfg");
336 fo = fopen("tmp.cfg", "r");
337 fn = fopen(fname, "w");
338 if (fo == NULL || fn == NULL) goto write;
340 // copy everything until sect
341 tlen = strlen(section);
344 tmp = fgets(line, sizeof(line), fo);
345 if (tmp == NULL) break;
347 if (line[0] == '[' && strncmp(line + 1, section, tlen) == 0 && line[tlen+1] == ']')
352 // now skip to next sect
355 tmp = fgets(line, sizeof(line), fo);
356 if (tmp == NULL) break;
357 if (line[0] == '[') {
358 fseek(fo, -strlen(line), SEEK_CUR);
364 fclose(fo); fo = NULL;
370 fn = fopen(fname, "w");
379 fprintf(fn, "[%s]" NL, section);
381 for (t = 0; t < sizeof(cfg_opts) / sizeof(cfg_opts[0]); t++)
384 tlen = *(cfg_opt_counts[t]);
385 for (i = 0; i < tlen; i++, me++)
387 if (!me->need_to_save) continue;
388 if ((me->beh != MB_ONOFF && me->beh != MB_RANGE) || me->name == NULL)
389 custom_write(fn, me, no_defaults);
390 else if (me->beh == MB_ONOFF) {
391 if (!no_defaults || ((*(int *)me->var ^ default_var(me)) & me->mask))
392 fprintf(fn, "%s = %i" NL, me->name, (*(int *)me->var & me->mask) ? 1 : 0);
393 } else if (me->beh == MB_RANGE) {
394 if (!no_defaults || (*(int *)me->var ^ default_var(me)))
395 fprintf(fn, "%s = %i" NL, me->name, *(int *)me->var);
400 /* input: save device names */
401 for (t = 0; t < IN_MAX_DEVS; t++)
403 const int *binds = in_get_dev_binds(t);
404 const char *name = in_get_dev_name(t);
405 if (binds == NULL || name == NULL)
408 fprintf(fn, "input%d = %s" NL, t, name);
411 /* input: save binds */
412 for (t = 0; t < IN_MAX_DEVS; t++)
414 const int *binds = in_get_dev_binds(t);
415 const char *name = in_get_dev_name(t);
419 if (binds == NULL || name == NULL)
422 sprintf(strbind, "bind%d", t);
423 if (t == 0) strbind[4] = 0;
425 count = in_get_dev_bind_count(t);
426 keys_write(fn, strbind, t, binds, no_defaults);
431 keys_write(fn, "bind", currentConfig.KeyBinds, defaultConfig.KeyBinds, keyNames, PLAT_MAX_KEYS, no_defaults);
433 keys_write(fn, "bind_joy0", currentConfig.JoyBinds[0], defaultConfig.JoyBinds[0], joyKeyNames, 32, 1);
434 keys_write(fn, "bind_joy1", currentConfig.JoyBinds[1], defaultConfig.JoyBinds[1], joyKeyNames, 32, 1);
435 keys_write(fn, "bind_joy2", currentConfig.JoyBinds[2], defaultConfig.JoyBinds[2], joyKeyNames, 32, 1);
436 keys_write(fn, "bind_joy3", currentConfig.JoyBinds[3], defaultConfig.JoyBinds[3], joyKeyNames, 32, 1);
442 fprintf(fn, "Sound Volume = %i" NL, currentConfig.volume);
449 // copy whatever is left
452 tmp = fgets(line, sizeof(line), fo);
453 if (tmp == NULL) break;
466 int config_writelrom(const char *fname)
468 char line[128], *tmp, *optr = NULL;
469 char *old_data = NULL;
473 if (strlen(loadedRomFName) == 0) return -1;
475 f = fopen(fname, "r");
478 fseek(f, 0, SEEK_END);
480 fseek(f, 0, SEEK_SET);
481 old_data = malloc(size + size/8);
482 if (old_data != NULL)
487 tmp = fgets(line, sizeof(line), f);
488 if (tmp == NULL) break;
490 if (strncasecmp(line, "LastUsedROM", 11) == 0)
492 sprintf(optr, "%s", line);
493 optr += strlen(optr);
499 f = fopen(fname, "w");
500 if (f == NULL) return -1;
502 if (old_data != NULL) {
503 fwrite(old_data, 1, optr - old_data, f);
506 fprintf(f, "LastUsedROM = %s" NL, loadedRomFName);
511 /* --------------------------------------------------------------------------*/
513 int config_readlrom(const char *fname)
515 char line[128], *tmp;
516 int i, len, ret = -1;
519 f = fopen(fname, "r");
520 if (f == NULL) return -1;
522 // seek to the section needed
525 tmp = fgets(line, sizeof(line), f);
526 if (tmp == NULL) break;
528 if (strncasecmp(line, "LastUsedROM", 11) != 0) continue;
530 for (i = 0; i < len; i++)
531 if (line[i] == '#' || line[i] == '\r' || line[i] == '\n') { line[i] = 0; break; }
532 tmp = strchr(line, '=');
533 if (tmp == NULL) break;
537 len = sizeof(loadedRomFName);
538 strncpy(loadedRomFName, tmp, len);
539 loadedRomFName[len-1] = 0;
548 static int custom_read(menu_entry *me, const char *var, const char *val)
555 case MA_OPT_RENDERER:
556 if (strcasecmp(var, "Renderer") != 0) return 0;
557 if (strcasecmp(val, "8bit fast") == 0 || strcasecmp(val, "fast") == 0) {
558 PicoOpt |= POPT_ALT_RENDERER;
560 else if (strcasecmp(val, "16bit accurate") == 0 || strcasecmp(val, "accurate") == 0) {
561 PicoOpt &= ~POPT_ALT_RENDERER;
562 currentConfig.EmuOpt |= 0x80;
564 else if (strcasecmp(val, "8bit accurate") == 0) {
565 PicoOpt &= ~POPT_ALT_RENDERER;
566 currentConfig.EmuOpt &= ~0x80;
574 if (strcasecmp(var, "Scaling") != 0) return 0;
575 if (strcasecmp(val, "OFF") == 0) {
576 currentConfig.scaling = 0;
577 } else if (strcasecmp(val, "hw horizontal") == 0) {
578 currentConfig.scaling = 1;
579 } else if (strcasecmp(val, "hw horiz. + vert.") == 0) {
580 currentConfig.scaling = 2;
581 } else if (strcasecmp(val, "sw horizontal") == 0) {
582 currentConfig.scaling = 3;
590 case MA_OPT_FRAMESKIP:
591 if (strcasecmp(var, "Frameskip") != 0) return 0;
592 if (strcasecmp(val, "Auto") == 0)
593 currentConfig.Frameskip = -1;
594 else currentConfig.Frameskip = atoi(val);
597 case MA_OPT_SOUND_QUALITY:
598 if (strcasecmp(var, "Sound Quality") != 0) return 0;
599 PsndRate = strtoul(val, &tmp, 10);
600 if (PsndRate < 8000 || PsndRate > 44100)
602 while (*tmp == ' ') tmp++;
603 if (strcasecmp(tmp, "stereo") == 0) {
604 PicoOpt |= POPT_EN_STEREO;
605 } else if (strcasecmp(tmp, "mono") == 0) {
606 PicoOpt &= ~POPT_EN_STEREO;
612 if (strcasecmp(var, "Region") != 0) return 0;
613 if (strncasecmp(val, "Auto: ", 6) == 0)
615 const char *p = val + 5, *end = val + strlen(val);
617 PicoRegionOverride = PicoAutoRgnOrder = 0;
618 for (i = 0; p < end && i < 3; i++)
620 while (*p == ' ') p++;
621 if (p[0] == 'J' && p[1] == 'P') {
622 PicoAutoRgnOrder |= 1 << (i*4);
623 } else if (p[0] == 'U' && p[1] == 'S') {
624 PicoAutoRgnOrder |= 4 << (i*4);
625 } else if (p[0] == 'E' && p[1] == 'U') {
626 PicoAutoRgnOrder |= 8 << (i*4);
628 while (*p != ' ' && *p != 0) p++;
632 else if (strcasecmp(val, "Auto") == 0) {
633 PicoRegionOverride = 0;
634 } else if (strcasecmp(val, "Japan NTSC") == 0) {
635 PicoRegionOverride = 1;
636 } else if (strcasecmp(val, "Japan PAL") == 0) {
637 PicoRegionOverride = 2;
638 } else if (strcasecmp(val, "USA") == 0) {
639 PicoRegionOverride = 4;
640 } else if (strcasecmp(val, "Europe") == 0) {
641 PicoRegionOverride = 8;
646 case MA_OPT_CONFIRM_STATES:
647 if (strcasecmp(var, "Confirm savestate") != 0) return 0;
648 if (strcasecmp(val, "OFF") == 0) {
649 currentConfig.EmuOpt &= ~(5<<9);
650 } else if (strcasecmp(val, "writes") == 0) {
651 currentConfig.EmuOpt &= ~(5<<9);
652 currentConfig.EmuOpt |= 1<<9;
653 } else if (strcasecmp(val, "loads") == 0) {
654 currentConfig.EmuOpt &= ~(5<<9);
655 currentConfig.EmuOpt |= 4<<9;
656 } else if (strcasecmp(val, "both") == 0) {
657 currentConfig.EmuOpt &= ~(5<<9);
658 currentConfig.EmuOpt |= 5<<9;
663 case MA_OPT_CPU_CLOCKS:
665 if (strcasecmp(var, "GP2X CPU clocks") != 0) return 0;
667 if (strcasecmp(var, "PSP CPU clock") != 0) return 0;
669 currentConfig.CPUclock = atoi(val);
673 if (strcasecmp(var, "Gamma correction") != 0) return 0;
674 currentConfig.gamma = (int) (atof(val) * 100.0);
677 case MA_OPT2_SQUIDGEHACK:
678 if (strcasecmp(var, "Squidgehack") != 0) return 0;
680 if (tmpi) *(int *)me->var |= me->mask;
681 else *(int *)me->var &= ~me->mask;
684 case MA_CDOPT_READAHEAD:
685 if (strcasecmp(var, "ReadAhead buffer") != 0) return 0;
686 PicoCDBuffers = atoi(val) / 2;
691 if (strcasecmp(var, "Scale factor") != 0) return 0;
692 currentConfig.scale = atof(val);
694 case MA_OPT3_HSCALE32:
695 if (strcasecmp(var, "Hor. scale (for low res. games)") != 0) return 0;
696 currentConfig.hscale32 = atof(val);
698 case MA_OPT3_HSCALE40:
699 if (strcasecmp(var, "Hor. scale (for hi res. games)") != 0) return 0;
700 currentConfig.hscale40 = atof(val);
702 case MA_OPT3_FILTERING:
703 if (strcasecmp(var, "Bilinear filtering") != 0) return 0;
704 currentConfig.scaling = atoi(val);
707 if (strcasecmp(var, "Gamma adjustment") != 0) return 0;
708 currentConfig.gamma = atoi(val);
710 case MA_OPT3_BLACKLVL:
711 if (strcasecmp(var, "Black level") != 0) return 0;
712 currentConfig.gamma2 = atoi(val);
715 if (strcasecmp(var, "Wait for vsync") != 0) return 0;
716 if (strcasecmp(val, "never") == 0) {
717 currentConfig.EmuOpt &= ~0x12000;
718 } else if (strcasecmp(val, "sometimes") == 0) {
719 currentConfig.EmuOpt |= 0x12000;
720 } else if (strcasecmp(val, "always") == 0) {
721 currentConfig.EmuOpt &= ~0x12000;
722 currentConfig.EmuOpt |= 0x02000;
728 lprintf("unhandled custom_read: %i\n", me->id);
734 static unsigned int keys_encountered = 0;
736 static int parse_bind_val(const char *val)
743 if (strncasecmp(val, "player", 6) == 0)
746 player = atoi(val + 6) - 1;
750 for (i = 0; i < sizeof(me_ctrl_actions) / sizeof(me_ctrl_actions[0]); i++) {
751 if (strncasecmp(me_ctrl_actions[i].name, val + 8, strlen(val + 8)) == 0)
752 return me_ctrl_actions[i].mask | (player<<16);
755 for (i = 0; emuctrl_actions[i].name != NULL; i++) {
756 if (strncasecmp(emuctrl_actions[i].name, val, strlen(val)) == 0)
757 return emuctrl_actions[i].mask;
763 static void keys_parse(const char *key, const char *val, int dev_id)
767 binds = parse_bind_val(val);
769 lprintf("config: unhandled action \"%s\"\n", val);
773 in_config_bind_key(dev_id, key, binds);
775 for (t = 0; t < 32; t++)
777 if (names[t] && strcmp(names[t], var) == 0) break;
781 int len = strlen(var);
782 if (len == 1) t = var[0];
783 else if (len >= 4 && var[0] == '\\' && var[1] == 'x') {
785 t = (int)strtoul(var + 2, &p, 16);
786 if (*p != 0) t = max_keys; // parse failed
789 t = max_keys; // invalid
791 if (t < 0 || t >= max_keys) {
792 lprintf("unhandled bind \"%s\"\n", var);
796 // unbind old, but only when key is first encountered
797 if (t < 32 && binds == currentConfig.KeyBinds && !(keys_encountered & (1<<t))) {
799 keys_encountered |= 1<<t;
805 #define try_joy_parse(num) { \
806 if (strncasecmp(var, "bind_joy"#num " ", 10) == 0) { \
807 keys_parse(var + 10, val, currentConfig.JoyBinds[num], joyKeyNames, 32); \
812 static int get_numvar_num(const char *var)
820 num = strtoul(var, &p, 10);
821 if (*p == 0 || *p == ' ')
827 /* map dev number in confing to input dev number */
828 static unsigned char input_dev_map[IN_MAX_DEVS];
830 static void parse(const char *var, const char *val)
833 int t, i, tlen, tmp, ret = 0;
835 if (strcasecmp(var, "LastUsedROM") == 0)
836 return; /* handled elsewhere */
838 if (strcasecmp(var, "Sound Volume") == 0) {
839 currentConfig.volume = atoi(val);
843 /* input: device name */
844 if (strncasecmp(var, "input", 5) == 0) {
845 int num = get_numvar_num(var + 5);
846 if (num >= 0 && num < IN_MAX_DEVS)
847 input_dev_map[num] = in_config_parse_dev(val);
849 printf("failed to parse: %s\n", var);
854 if (strncasecmp(var, "bind", 4) == 0) {
855 const char *p = var + 4;
856 int num = get_numvar_num(p);
857 if (num < 0 || num >= IN_MAX_DEVS) {
858 printf("failed to parse: %s\n", var);
862 num = input_dev_map[num];
863 if (num < 0 || num >= IN_MAX_DEVS) {
864 printf("invalid device id: %s\n", var);
868 while (*p && *p != ' ') p++;
869 while (*p && *p == ' ') p++;
870 keys_parse(p, val, num);
881 for (t = 0; t < sizeof(cfg_opts) / sizeof(cfg_opts[0]) && ret == 0; t++)
884 tlen = *(cfg_opt_counts[t]);
885 for (i = 0; i < tlen && ret == 0; i++, me++)
887 if (!me->need_to_save) continue;
888 if (me->name != NULL) {
889 if (strcasecmp(var, me->name) != 0) continue; // surely not this one
890 if (me->beh == MB_ONOFF) {
892 if (tmp) *(int *)me->var |= me->mask;
893 else *(int *)me->var &= ~me->mask;
895 } else if (me->beh == MB_RANGE) {
897 if (tmp < me->min) tmp = me->min;
898 if (tmp > me->max) tmp = me->max;
899 *(int *)me->var = tmp;
903 ret = custom_read(me, var, val);
906 if (!ret) lprintf("config_readsect: unhandled var: \"%s\"\n", var);
910 int config_havesect(const char *fname, const char *section)
915 f = fopen(fname, "r");
916 if (f == NULL) return 0;
918 ret = seek_sect(f, section);
923 int config_readsect(const char *fname, const char *section)
925 char line[128], *var, *val;
929 f = fopen(fname, "r");
930 if (f == NULL) return -1;
934 ret = seek_sect(f, section);
936 lprintf("config_readsect: %s: missing section [%s]\n", fname, section);
942 keys_encountered = 0;
943 memset(input_dev_map, 0xff, sizeof(input_dev_map));
948 ret = config_get_var_val(f, line, sizeof(line), &var, &val);
950 if (ret == -1) continue;
962 static char *mystrip(char *str)
967 for (i = 0; i < len; i++)
968 if (str[i] != ' ') break;
969 if (i > 0) memmove(str, str + i, len - i + 1);
972 for (i = len - 1; i >= 0; i--)
973 if (str[i] != ' ') break;
982 * -1 - failed to parse line
984 int config_get_var_val(void *file, char *line, int lsize, char **rvar, char **rval)
986 char *var, *val, *tmp;
990 tmp = fgets(line, lsize, f);
991 if (tmp == NULL) return 0;
993 if (line[0] == '[') return 0; // other section
995 // strip comments, linefeed, spaces..
997 for (i = 0; i < len; i++)
998 if (line[i] == '#' || line[i] == '\r' || line[i] == '\n') { line[i] = 0; break; }
1001 if (len <= 0) return -1;;
1004 for (i = 0; i < len; i++)
1005 if (line[i] == '=') break;
1006 if (i >= len || strchr(&line[i+1], '=') != NULL) {
1007 lprintf("config_readsect: can't parse: %s\n", line);
1017 if (strlen(var) == 0 || (strlen(val) == 0 && strncasecmp(var, "bind", 4) != 0)) {
1018 lprintf("config_readsect: something's empty: \"%s\" = \"%s\"\n", var, val);