1 // This parses the RC file.
2
3 #ifdef __MINGW32__
4 #undef __STRICT_ANSI__
5 #endif
6
7 #include <stddef.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <SDL.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <errno.h>
15 #include <assert.h>
16 #include <strings.h>
17 #include <ctype.h>
18
19 #include "rc.h"
20 #include "ckvp.h"
21 #include "pd-defs.h"
22 #include "romload.h"
23 #include "system.h"
24 #include "md.h"
25
26 // CTV names
27 const char *ctv_names[] = {
28 "off", "blur", "scanline", "interlace", "swab",
29 NULL
30 };
31
32 // Scaling algorithms names
33 const char *scaling_names[] = {
34 "stretch",
35 "scale",
36 "hqx",
37 "hqx stretch",
38 "scale2x",
39 "scale2x stretch",
40 "none",
41 NULL
42 };
43
44 // CPU names, keep index in sync with rc-vars.h and enums in md.h
45 const char *emu_z80_names[] = { "none", "mz80", "cz80", "drz80", NULL };
46 const char *emu_m68k_names[] = { "none", "star", "musa", "cyclone", NULL };
47
48 // The table of strings and the keysyms they map to.
49 // The order is a bit weird, since this was originally a mapping for the SVGALib
50 // scancodes, and I just added the SDL stuff on top of it.
51 struct rc_keysym rc_keysyms[] = {
52 { "ESCAPE", PDK_ESCAPE },
53 { "BACKSPACE", PDK_BACKSPACE },
54 { "TAB", PDK_TAB },
55 { "RETURN", PDK_RETURN },
56 { "ENTER", PDK_RETURN },
57 { "KP_MULTIPLY", PDK_KP_MULTIPLY },
58 { "SPACE", PDK_SPACE },
59 { "F1", PDK_F1 },
60 { "F2", PDK_F2 },
61 { "F3", PDK_F3 },
62 { "F4", PDK_F4 },
63 { "F5", PDK_F5 },
64 { "F6", PDK_F6 },
65 { "F7", PDK_F7 },
66 { "F8", PDK_F8 },
67 { "F9", PDK_F9 },
68 { "F10", PDK_F10 },
69 { "KP_7", PDK_KP7 },
70 { "KP_HOME", PDK_KP7 },
71 { "KP_8", PDK_KP8 },
72 { "KP_UP", PDK_KP8 },
73 { "KP_9", PDK_KP9 },
74 { "KP_PAGE_UP", PDK_KP9 },
75 { "KP_PAGEUP", PDK_KP9 },
76 { "KP_MINUS", PDK_KP_MINUS },
77 { "KP_4", PDK_KP4 },
78 { "KP_LEFT", PDK_KP4 },
79 { "KP_5", PDK_KP5 },
80 { "KP_6", PDK_KP6 },
81 { "KP_RIGHT", PDK_KP6 },
82 { "KP_PLUS", PDK_KP_PLUS },
83 { "KP_1", PDK_KP1 },
84 { "KP_END", PDK_KP1 },
85 { "KP_2", PDK_KP2 },
86 { "KP_DOWN", PDK_KP2 },
87 { "KP_3", PDK_KP3 },
88 { "KP_PAGE_DOWN", PDK_KP3 },
89 { "KP_PAGEDOWN", PDK_KP3 },
90 { "KP_0", PDK_KP0 },
91 { "KP_INSERT", PDK_KP0 },
92 { "KP_PERIOD", PDK_KP_PERIOD },
93 { "KP_DELETE", PDK_KP_PERIOD },
94 { "F11", PDK_F11 },
95 { "F12", PDK_F12 },
96 { "KP_ENTER", PDK_KP_ENTER },
97 { "KP_DIVIDE", PDK_KP_DIVIDE },
98 { "HOME", PDK_HOME },
99 { "UP", PDK_UP },
100 { "PAGE_UP", PDK_PAGEUP },
101 { "PAGEUP", PDK_PAGEUP },
102 { "LEFT", PDK_LEFT },
103 { "RIGHT", PDK_RIGHT },
104 { "END", PDK_END },
105 { "DOWN", PDK_DOWN },
106 { "PAGE_DOWN", PDK_PAGEDOWN },
107 { "PAGEDOWN", PDK_PAGEDOWN },
108 { "INSERT", PDK_INSERT },
109 { "DELETE", PDK_DELETE },
110 { "NUMLOCK", PDK_NUMLOCK },
111 { "NUM_LOCK", PDK_NUMLOCK },
112 { "CAPSLOCK", PDK_CAPSLOCK },
113 { "CAPS_LOCK", PDK_CAPSLOCK },
114 { "SCROLLOCK", PDK_SCROLLOCK },
115 { "SCROLL_LOCK", PDK_SCROLLOCK },
116 { "LSHIFT", PDK_LSHIFT },
117 { "SHIFT_L", PDK_LSHIFT },
118 { "RSHIFT", PDK_RSHIFT },
119 { "SHIFT_R", PDK_RSHIFT },
120 { "LCTRL", PDK_LCTRL },
121 { "CTRL_L", PDK_LCTRL },
122 { "RCTRL", PDK_RCTRL },
123 { "CTRL_R", PDK_RCTRL },
124 { "LALT", PDK_LALT },
125 { "ALT_L", PDK_LALT },
126 { "RALT", PDK_RALT },
127 { "ALT_R", PDK_RALT },
128 { "LMETA", PDK_LMETA },
129 { "META_L", PDK_LMETA },
130 { "RMETA", PDK_RMETA },
131 { "META_R", PDK_RMETA },
132 { "", 0 },
133 { NULL, 0 } // Terminator
134 }; // Phew! ;)
135
136 /* Define all the external RC variables */
137 #include "rc-vars.h"
138
139 static const struct {
140 const char *name;
141 uint32_t flag;
142 } keymod[] = {
143 { "shift-", KEYSYM_MOD_SHIFT },
144 { "ctrl-", KEYSYM_MOD_CTRL },
145 { "alt-", KEYSYM_MOD_ALT },
146 { "meta-", KEYSYM_MOD_META },
147 { "", 0 }
148 };
149
150 /* Parse a keysym.
151 * If the string matches one of the strings in the keysym table above
152 * or if it's another valid character, return the keysym, otherwise -1. */
rc_keysym(const char * code,intptr_t *)153 intptr_t rc_keysym(const char *code, intptr_t *)
154 {
155 struct rc_keysym *ks = rc_keysyms;
156 uint32_t m = 0;
157 uint32_t c;
158
159 while (*code != '\0') {
160 size_t i;
161
162 for (i = 0; (keymod[i].name[0] != '\0'); ++i) {
163 size_t l = strlen(keymod[i].name);
164
165 if (strncasecmp(keymod[i].name, code, l))
166 continue;
167 m |= keymod[i].flag;
168 code += l;
169 break;
170 }
171 if (keymod[i].name[0] == '\0')
172 break;
173 }
174 while (ks->name != NULL) {
175 if (!strcasecmp(ks->name, code))
176 return (m | ks->keysym);
177 ++ks;
178 }
179 /* Not in the list, so expect a single UTF-8 character instead. */
180 code += utf8u32(&c, (const uint8_t *)code);
181 if ((c == (uint32_t)-1) || (*code != '\0'))
182 return -1;
183 /* Must fit in 16 bits. */
184 if (c > 0xffff)
185 return -1;
186 return (m | (c & 0xffff));
187 }
188
189 /* Convert a keysym to text. */
dump_keysym(intptr_t k)190 char *dump_keysym(intptr_t k)
191 {
192 char buf[64];
193 size_t i;
194 size_t n;
195 size_t l = 0;
196 struct rc_keysym *ks = rc_keysyms;
197
198 buf[0] = '\0';
199 for (i = 0; ((keymod[i].name[0] != '\0') && (l < sizeof(buf))); ++i)
200 if (k & keymod[i].flag) {
201 n = strlen(keymod[i].name);
202 if (n > (sizeof(buf) - l))
203 n = (sizeof(buf) - l);
204 memcpy(&buf[l], keymod[i].name, n);
205 l += n;
206 }
207 k &= ~KEYSYM_MOD_MASK;
208 while (ks->name != NULL) {
209 if (ks->keysym == k) {
210 n = strlen(ks->name);
211 if (n > (sizeof(buf) - l))
212 n = (sizeof(buf) - l);
213 memcpy(&buf[l], ks->name, n);
214 l += n;
215 goto found;
216 }
217 ++ks;
218 }
219 n = utf32u8(NULL, k);
220 if ((n == 0) || (n > (sizeof(buf) - l)))
221 return NULL;
222 utf32u8((uint8_t *)&buf[l], k);
223 l += n;
224 found:
225 return backslashify((uint8_t *)buf, l, 0, NULL);
226 }
227
228 /* Parse a boolean value.
229 * If the string is "yes" or "true", return 1.
230 * If the string is "no" or "false", return 0.
231 * Otherwise, just return atoi(value). */
rc_boolean(const char * value,intptr_t *)232 intptr_t rc_boolean(const char *value, intptr_t *)
233 {
234 if(!strcasecmp(value, "yes") || !strcasecmp(value, "true"))
235 return 1;
236 if(!strcasecmp(value, "no") || !strcasecmp(value, "false"))
237 return 0;
238 return atoi(value);
239 }
240
241 static const char *joypad_axis_type[] = {
242 "max", "p", "positive",
243 "min", "n", "negative",
244 "between", "b", NULL
245 };
246
247 static const unsigned int joypad_axis_value[] = {
248 JS_AXIS_POSITIVE, JS_AXIS_POSITIVE, JS_AXIS_POSITIVE,
249 JS_AXIS_NEGATIVE, JS_AXIS_NEGATIVE, JS_AXIS_NEGATIVE,
250 JS_AXIS_BETWEEN, JS_AXIS_BETWEEN
251 };
252
253 static const char *joypad_hat_type[] = {
254 "centered", "up", "right", "down", "left"
255 };
256
257 static const unsigned int joypad_hat_value[] = {
258 JS_HAT_CENTERED, JS_HAT_UP, JS_HAT_RIGHT, JS_HAT_DOWN, JS_HAT_LEFT
259 };
260
261 /* Convert a joypad entry to text. */
dump_joypad(intptr_t js)262 char *dump_joypad(intptr_t js)
263 {
264 char *str = NULL;
265 unsigned int id = JS_GET_IDENTIFIER(js);
266 const char *arg0 = NULL;
267 unsigned int val0 = 0;
268 const char *arg1 = NULL;
269 unsigned int i;
270
271 if (JS_IS_BUTTON(js)) {
272 arg0 = "button";
273 val0 = JS_GET_BUTTON(js);
274 }
275 else if (JS_IS_AXIS(js)) {
276 arg0 = "axis";
277 val0 = JS_GET_AXIS(js);
278 for (i = 0; (i != elemof(joypad_axis_value)); ++i)
279 if (joypad_axis_value[i] == JS_GET_AXIS_DIR(js)) {
280 arg1 = joypad_axis_type[i];
281 break;
282 }
283 if (arg1 == NULL)
284 return NULL;
285 }
286 else if (JS_IS_HAT(js)) {
287 arg0 = "hat";
288 val0 = JS_GET_HAT(js);
289 for (i = 0; (i != elemof(joypad_hat_value)); ++i)
290 if (joypad_hat_value[i] == JS_GET_HAT_DIR(js)) {
291 arg1 = joypad_hat_type[i];
292 break;
293 }
294 if (arg1 == NULL)
295 return NULL;
296 }
297 else
298 return NULL;
299 i = 0;
300 while (1) {
301 i = snprintf(str, i, "joystick%u-%s%u%s%s",
302 id, arg0, val0,
303 (arg1 ? "-" : ""), (arg1 ? arg1 : ""));
304 if ((str != NULL) ||
305 ((str = (char *)malloc(++i)) == NULL))
306 break;
307 }
308 return str;
309 }
310
311 /*
312 * Parse a joystick/joypad command (joy_*). The following syntaxes are
313 * supported:
314 *
315 * Normal buttons:
316 * (j|js|joystick|joypad)X-(b|button)Y
317 * Axes:
318 * (j|js|joystick|joypad)X-(a|axis)Y-(max|p|positive|min|n|negative)
319 * Hats:
320 * (j|js|joystick|joypad)X-(h|hat)Y-(up|right|down|left)
321 */
rc_joypad(const char * value,intptr_t *)322 intptr_t rc_joypad(const char *value, intptr_t *)
323 {
324 static const char *r_js[] = { "j", "js", "joystick", "joypad", NULL };
325 static const char *r_button[] = { "b", "button", NULL };
326 static const char *r_axis[] = { "a", "axis", NULL };
327 static const char **r_axis_type = joypad_axis_type;
328 static const unsigned int *r_axis_value = joypad_axis_value;
329 static const char *r_hat[] = { "h", "hat", NULL };
330 static const char **r_hat_type = joypad_hat_type;
331 static const unsigned int *r_hat_value = joypad_hat_value;
332 int i;
333 unsigned int id;
334 unsigned int arg;
335
336 if (*value == '\0')
337 return 0;
338 if ((i = prefix_casematch(value, r_js)) == -1)
339 return -1;
340 value += strlen(r_js[i]);
341 if ((i = prefix_getuint(value, &id)) == 0)
342 return -1;
343 value += i;
344 if (*value != '-')
345 return -1;
346 ++value;
347 if ((i = prefix_casematch(value, r_button)) != -1) {
348 value += strlen(r_button[i]);
349 if ((i = prefix_getuint(value, &arg)) == 0)
350 return -1;
351 value += i;
352 if (*value != '\0')
353 return -1;
354 return JS_BUTTON(id, arg);
355 }
356 if ((i = prefix_casematch(value, r_axis)) != -1) {
357 value += strlen(r_axis[i]);
358 if ((i = prefix_getuint(value, &arg)) == 0)
359 return -1;
360 value += i;
361 if (*value != '-')
362 return -1;
363 ++value;
364 if ((i = prefix_casematch(value, r_axis_type)) == -1)
365 return -1;
366 value += strlen(r_axis_type[i]);
367 if (*value != '\0')
368 return -1;
369 return JS_AXIS(id, arg, r_axis_value[i]);
370 }
371 if ((i = prefix_casematch(value, r_hat)) != -1) {
372 value += strlen(r_hat[i]);
373 if ((i = prefix_getuint(value, &arg)) == 0)
374 return -1;
375 value += i;
376 if (*value != '-')
377 return -1;
378 ++value;
379 if ((i = prefix_casematch(value, r_hat_type)) == -1)
380 return -1;
381 value += strlen(r_hat_type[i]);
382 if (*value != '\0')
383 return -1;
384 return JS_HAT(id, arg, r_hat_value[i]);
385 }
386 return -1;
387 }
388
389 static const char *mouse_motion_type[] = {
390 "up", "u",
391 "down", "d",
392 "left", "l",
393 "right", "r",
394 NULL
395 };
396
397 /* Convert a mouse entry to text. */
dump_mouse(intptr_t mo)398 char *dump_mouse(intptr_t mo)
399 {
400 char *str = NULL;
401 unsigned int id = MO_GET_IDENTIFIER(mo);
402 const char *arg0 = NULL;
403 unsigned int val0 = 0;
404 unsigned int i;
405
406 if (MO_IS_BUTTON(mo)) {
407 arg0 = "button";
408 val0 = MO_GET_BUTTON(mo);
409 }
410 else if (MO_IS_MOTION(mo)) {
411 val0 = MO_GET_MOTION(mo);
412 for (i = 0; (i != elemof(mouse_motion_type)); ++i)
413 if (mouse_motion_type[i][0] == (char)val0) {
414 arg0 = mouse_motion_type[i];
415 break;
416 }
417 if (arg0 == NULL)
418 return NULL;
419 }
420 else
421 return NULL;
422 i = 0;
423 while (1) {
424 if (MO_IS_BUTTON(mo))
425 i = snprintf(str, i, "mouse%u-%s%u", id, arg0, val0);
426 else
427 i = snprintf(str, i, "mouse%u-%s", id, arg0);
428 if ((str != NULL) ||
429 ((str = (char *)malloc(++i)) == NULL))
430 break;
431 }
432 return str;
433 }
434
435 /*
436 * Parse a mouse command (mou_*). The following syntaxes are
437 * supported:
438 *
439 * Buttons:
440 * (m|mo|mouse)X-(b|button)Y
441 * Motions:
442 * (m|mo|mouse)X-(u|up|d|down|l|left|r|right)
443 */
rc_mouse(const char * value,intptr_t *)444 intptr_t rc_mouse(const char *value, intptr_t *)
445 {
446 static const char *r_mo[] = { "m", "mo", "mouse", NULL };
447 static const char *r_button[] = { "b", "button", NULL };
448 static const char **r_motion_type = mouse_motion_type;
449 int i;
450 unsigned int id;
451 unsigned int arg;
452
453 if (*value == '\0')
454 return 0;
455 if ((i = prefix_casematch(value, r_mo)) == -1)
456 return -1;
457 value += strlen(r_mo[i]);
458 if ((i = prefix_getuint(value, &id)) == 0)
459 return -1;
460 value += i;
461 if (*value != '-')
462 return -1;
463 ++value;
464 if ((i = prefix_casematch(value, r_button)) != -1) {
465 value += strlen(r_button[i]);
466 if ((i = prefix_getuint(value, &arg)) == 0)
467 return -1;
468 value += i;
469 if (*value != '\0')
470 return -1;
471 return MO_BUTTON(id, arg);
472 }
473 if ((i = prefix_casematch(value, r_motion_type)) != -1) {
474 arg = r_motion_type[i][0];
475 value += strlen(r_motion_type[i]);
476 if (*value != '\0')
477 return -1;
478 return MO_MOTION(id, arg);
479 }
480 return -1;
481 }
482
483 /* Parse the CTV type. As new CTV filters get submitted expect this to grow ;)
484 * Current values are:
485 * off - No CTV
486 * blur - blur bitmap (from DirectX DGen), by Dave <dave@dtmnt.com>
487 * scanline - attenuates every other line, looks cool! by Phillip K. Hornung <redx@pknet.com>
488 */
rc_ctv(const char * value,intptr_t *)489 intptr_t rc_ctv(const char *value, intptr_t *)
490 {
491 for(int i = 0; i < NUM_CTV; ++i)
492 if(!strcasecmp(value, ctv_names[i])) return i;
493 return -1;
494 }
495
rc_scaling(const char * value,intptr_t *)496 intptr_t rc_scaling(const char *value, intptr_t *)
497 {
498 int i;
499
500 for (i = 0; (i < NUM_SCALING); ++i)
501 if (!strcasecmp(value, scaling_names[i]))
502 return i;
503 return -1;
504 }
505
506 /* Parse CPU types */
rc_emu_z80(const char * value,intptr_t *)507 intptr_t rc_emu_z80(const char *value, intptr_t *)
508 {
509 unsigned int i;
510
511 for (i = 0; (emu_z80_names[i] != NULL); ++i)
512 if (!strcasecmp(value, emu_z80_names[i]))
513 return i;
514 return -1;
515 }
516
rc_emu_m68k(const char * value,intptr_t *)517 intptr_t rc_emu_m68k(const char *value, intptr_t *)
518 {
519 unsigned int i;
520
521 for (i = 0; (emu_m68k_names[i] != NULL); ++i)
522 if (!strcasecmp(value, emu_m68k_names[i]))
523 return i;
524 return -1;
525 }
526
rc_region(const char * value,intptr_t *)527 intptr_t rc_region(const char *value, intptr_t *)
528 {
529 if (strlen(value) != 1)
530 return -1;
531 switch (value[0] | 0x20) {
532 case 'j':
533 case 'x':
534 case 'u':
535 case 'e':
536 case ' ':
537 return (value[0] & ~0x20);
538 }
539 return -1;
540 }
541
rc_string(const char * value,intptr_t *)542 intptr_t rc_string(const char *value, intptr_t *)
543 {
544 char *val;
545
546 if ((val = strdup(value)) == NULL)
547 return -1;
548 // -1 is reserved, thus invalid. Should not happen anyway.
549 if ((intptr_t)val == -1)
550 abort();
551 return (intptr_t)val;
552 }
553
rc_rom_path(const char * value,intptr_t *)554 intptr_t rc_rom_path(const char *value, intptr_t *)
555 {
556 intptr_t r = rc_string(value, NULL);
557
558 if (r == -1)
559 return -1;
560 set_rom_path((char *)r);
561 return r;
562 }
563
rc_number(const char * value,intptr_t *)564 intptr_t rc_number(const char *value, intptr_t *)
565 {
566 return strtol(value, NULL, 0);
567 }
568
rc_soundrate(const char * value,intptr_t *)569 intptr_t rc_soundrate(const char *value, intptr_t *)
570 {
571 long r = strtol(value, NULL, 0);
572
573 if (r < 8000)
574 r = 8000;
575 return r;
576 }
577
578 /* This is a table of all the RC options, the variables they affect, and the
579 * functions to parse their values. */
580 struct rc_field rc_fields[RC_FIELDS_SIZE] = {
581 { "key_pad1_up", rc_keysym, &pad1_up[RCBK] },
582 { "joy_pad1_up", rc_joypad, &pad1_up[RCBJ] },
583 { "mou_pad1_up", rc_mouse, &pad1_up[RCBM] },
584 { "key_pad1_down", rc_keysym, &pad1_down[RCBK] },
585 { "joy_pad1_down", rc_joypad, &pad1_down[RCBJ] },
586 { "mou_pad1_down", rc_mouse, &pad1_down[RCBM] },
587 { "key_pad1_left", rc_keysym, &pad1_left[RCBK] },
588 { "joy_pad1_left", rc_joypad, &pad1_left[RCBJ] },
589 { "mou_pad1_left", rc_mouse, &pad1_left[RCBM] },
590 { "key_pad1_right", rc_keysym, &pad1_right[RCBK] },
591 { "joy_pad1_right", rc_joypad, &pad1_right[RCBJ] },
592 { "mou_pad1_right", rc_mouse, &pad1_right[RCBM] },
593 { "key_pad1_a", rc_keysym, &pad1_a[RCBK] },
594 { "joy_pad1_a", rc_joypad, &pad1_a[RCBJ] },
595 { "mou_pad1_a", rc_mouse, &pad1_a[RCBM] },
596 { "key_pad1_b", rc_keysym, &pad1_b[RCBK] },
597 { "joy_pad1_b", rc_joypad, &pad1_b[RCBJ] },
598 { "mou_pad1_b", rc_mouse, &pad1_b[RCBM] },
599 { "key_pad1_c", rc_keysym, &pad1_c[RCBK] },
600 { "joy_pad1_c", rc_joypad, &pad1_c[RCBJ] },
601 { "mou_pad1_c", rc_mouse, &pad1_c[RCBM] },
602 { "key_pad1_x", rc_keysym, &pad1_x[RCBK] },
603 { "joy_pad1_x", rc_joypad, &pad1_x[RCBJ] },
604 { "mou_pad1_x", rc_mouse, &pad1_x[RCBM] },
605 { "key_pad1_y", rc_keysym, &pad1_y[RCBK] },
606 { "joy_pad1_y", rc_joypad, &pad1_y[RCBJ] },
607 { "mou_pad1_y", rc_mouse, &pad1_y[RCBM] },
608 { "key_pad1_z", rc_keysym, &pad1_z[RCBK] },
609 { "joy_pad1_z", rc_joypad, &pad1_z[RCBJ] },
610 { "mou_pad1_z", rc_mouse, &pad1_z[RCBM] },
611 { "key_pad1_mode", rc_keysym, &pad1_mode[RCBK] },
612 { "joy_pad1_mode", rc_joypad, &pad1_mode[RCBJ] },
613 { "mou_pad1_mode", rc_mouse, &pad1_mode[RCBM] },
614 { "key_pad1_start", rc_keysym, &pad1_start[RCBK] },
615 { "joy_pad1_start", rc_joypad, &pad1_start[RCBJ] },
616 { "mou_pad1_start", rc_mouse, &pad1_start[RCBM] },
617 { "key_pad2_up", rc_keysym, &pad2_up[RCBK] },
618 { "joy_pad2_up", rc_joypad, &pad2_up[RCBJ] },
619 { "mou_pad2_up", rc_mouse, &pad2_up[RCBM] },
620 { "key_pad2_down", rc_keysym, &pad2_down[RCBK] },
621 { "joy_pad2_down", rc_joypad, &pad2_down[RCBJ] },
622 { "mou_pad2_down", rc_mouse, &pad2_down[RCBM] },
623 { "key_pad2_left", rc_keysym, &pad2_left[RCBK] },
624 { "joy_pad2_left", rc_joypad, &pad2_left[RCBJ] },
625 { "mou_pad2_left", rc_mouse, &pad2_left[RCBM] },
626 { "key_pad2_right", rc_keysym, &pad2_right[RCBK] },
627 { "joy_pad2_right", rc_joypad, &pad2_right[RCBJ] },
628 { "mou_pad2_right", rc_mouse, &pad2_right[RCBM] },
629 { "key_pad2_a", rc_keysym, &pad2_a[RCBK] },
630 { "joy_pad2_a", rc_joypad, &pad2_a[RCBJ] },
631 { "mou_pad2_a", rc_mouse, &pad2_a[RCBM] },
632 { "key_pad2_b", rc_keysym, &pad2_b[RCBK] },
633 { "joy_pad2_b", rc_joypad, &pad2_b[RCBJ] },
634 { "mou_pad2_b", rc_mouse, &pad2_b[RCBM] },
635 { "key_pad2_c", rc_keysym, &pad2_c[RCBK] },
636 { "joy_pad2_c", rc_joypad, &pad2_c[RCBJ] },
637 { "mou_pad2_c", rc_mouse, &pad2_c[RCBM] },
638 { "key_pad2_x", rc_keysym, &pad2_x[RCBK] },
639 { "joy_pad2_x", rc_joypad, &pad2_x[RCBJ] },
640 { "mou_pad2_x", rc_mouse, &pad2_x[RCBM] },
641 { "key_pad2_y", rc_keysym, &pad2_y[RCBK] },
642 { "joy_pad2_y", rc_joypad, &pad2_y[RCBJ] },
643 { "mou_pad2_y", rc_mouse, &pad2_y[RCBM] },
644 { "key_pad2_z", rc_keysym, &pad2_z[RCBK] },
645 { "joy_pad2_z", rc_joypad, &pad2_z[RCBJ] },
646 { "mou_pad2_z", rc_mouse, &pad2_z[RCBM] },
647 { "key_pad2_mode", rc_keysym, &pad2_mode[RCBK] },
648 { "joy_pad2_mode", rc_joypad, &pad2_mode[RCBJ] },
649 { "mou_pad2_mode", rc_mouse, &pad2_mode[RCBM] },
650 { "key_pad2_start", rc_keysym, &pad2_start[RCBK] },
651 { "joy_pad2_start", rc_joypad, &pad2_start[RCBJ] },
652 { "mou_pad2_start", rc_mouse, &pad2_start[RCBM] },
653 { "key_pico_pen_up", rc_keysym, &pico_pen_up[RCBK] },
654 { "joy_pico_pen_up", rc_joypad, &pico_pen_up[RCBJ] },
655 { "mou_pico_pen_up", rc_mouse, &pico_pen_up[RCBM] },
656 { "key_pico_pen_down", rc_keysym, &pico_pen_down[RCBK] },
657 { "joy_pico_pen_down", rc_joypad, &pico_pen_down[RCBJ] },
658 { "mou_pico_pen_down", rc_mouse, &pico_pen_down[RCBM] },
659 { "key_pico_pen_left", rc_keysym, &pico_pen_left[RCBK] },
660 { "joy_pico_pen_left", rc_joypad, &pico_pen_left[RCBJ] },
661 { "mou_pico_pen_left", rc_mouse, &pico_pen_left[RCBM] },
662 { "key_pico_pen_right", rc_keysym, &pico_pen_right[RCBK] },
663 { "joy_pico_pen_right", rc_joypad, &pico_pen_right[RCBJ] },
664 { "mou_pico_pen_right", rc_mouse, &pico_pen_right[RCBM] },
665 { "key_pico_pen_button", rc_keysym, &pico_pen_button[RCBK] },
666 { "joy_pico_pen_button", rc_joypad, &pico_pen_button[RCBJ] },
667 { "mou_pico_pen_button", rc_mouse, &pico_pen_button[RCBM] },
668 { "int_pico_pen_stride", rc_number, &pico_pen_stride },
669 { "int_pico_pen_delay", rc_number, &pico_pen_delay },
670 { "key_fix_checksum", rc_keysym, &dgen_fix_checksum[RCBK] },
671 { "joy_fix_checksum", rc_joypad, &dgen_fix_checksum[RCBJ] },
672 { "mou_fix_checksum", rc_mouse, &dgen_fix_checksum[RCBM] },
673 { "key_quit", rc_keysym, &dgen_quit[RCBK] },
674 { "joy_quit", rc_joypad, &dgen_quit[RCBJ] },
675 { "mou_quit", rc_mouse, &dgen_quit[RCBM] },
676 { "key_craptv_toggle", rc_keysym, &dgen_craptv_toggle[RCBK] },
677 { "joy_craptv_toggle", rc_joypad, &dgen_craptv_toggle[RCBJ] },
678 { "mou_craptv_toggle", rc_mouse, &dgen_craptv_toggle[RCBM] },
679 { "key_scaling_toggle", rc_keysym, &dgen_scaling_toggle[RCBK] },
680 { "joy_scaling_toggle", rc_joypad, &dgen_scaling_toggle[RCBJ] },
681 { "mou_scaling_toggle", rc_mouse, &dgen_scaling_toggle[RCBM] },
682 { "key_screenshot", rc_keysym, &dgen_screenshot[RCBK] },
683 { "joy_screenshot", rc_joypad, &dgen_screenshot[RCBJ] },
684 { "mou_screenshot", rc_mouse, &dgen_screenshot[RCBM] },
685 { "key_reset", rc_keysym, &dgen_reset[RCBK] },
686 { "joy_reset", rc_joypad, &dgen_reset[RCBJ] },
687 { "mou_reset", rc_mouse, &dgen_reset[RCBM] },
688 { "key_slot_0", rc_keysym, &dgen_slot_0[RCBK] },
689 { "joy_slot_0", rc_joypad, &dgen_slot_0[RCBJ] },
690 { "mou_slot_0", rc_mouse, &dgen_slot_0[RCBM] },
691 { "key_slot_1", rc_keysym, &dgen_slot_1[RCBK] },
692 { "joy_slot_1", rc_joypad, &dgen_slot_1[RCBJ] },
693 { "mou_slot_1", rc_mouse, &dgen_slot_1[RCBM] },
694 { "key_slot_2", rc_keysym, &dgen_slot_2[RCBK] },
695 { "joy_slot_2", rc_joypad, &dgen_slot_2[RCBJ] },
696 { "mou_slot_2", rc_mouse, &dgen_slot_2[RCBM] },
697 { "key_slot_3", rc_keysym, &dgen_slot_3[RCBK] },
698 { "joy_slot_3", rc_joypad, &dgen_slot_3[RCBJ] },
699 { "mou_slot_3", rc_mouse, &dgen_slot_3[RCBM] },
700 { "key_slot_4", rc_keysym, &dgen_slot_4[RCBK] },
701 { "joy_slot_4", rc_joypad, &dgen_slot_4[RCBJ] },
702 { "mou_slot_4", rc_mouse, &dgen_slot_4[RCBM] },
703 { "key_slot_5", rc_keysym, &dgen_slot_5[RCBK] },
704 { "joy_slot_5", rc_joypad, &dgen_slot_5[RCBJ] },
705 { "mou_slot_5", rc_mouse, &dgen_slot_5[RCBM] },
706 { "key_slot_6", rc_keysym, &dgen_slot_6[RCBK] },
707 { "joy_slot_6", rc_joypad, &dgen_slot_6[RCBJ] },
708 { "mou_slot_6", rc_mouse, &dgen_slot_6[RCBM] },
709 { "key_slot_7", rc_keysym, &dgen_slot_7[RCBK] },
710 { "joy_slot_7", rc_joypad, &dgen_slot_7[RCBJ] },
711 { "mou_slot_7", rc_mouse, &dgen_slot_7[RCBM] },
712 { "key_slot_8", rc_keysym, &dgen_slot_8[RCBK] },
713 { "joy_slot_8", rc_joypad, &dgen_slot_8[RCBJ] },
714 { "mou_slot_8", rc_mouse, &dgen_slot_8[RCBM] },
715 { "key_slot_9", rc_keysym, &dgen_slot_9[RCBK] },
716 { "joy_slot_9", rc_joypad, &dgen_slot_9[RCBJ] },
717 { "mou_slot_9", rc_mouse, &dgen_slot_9[RCBM] },
718 { "key_slot_next", rc_keysym, &dgen_slot_next[RCBK] },
719 { "joy_slot_next", rc_joypad, &dgen_slot_next[RCBJ] },
720 { "mou_slot_next", rc_mouse, &dgen_slot_next[RCBM] },
721 { "key_slot_prev", rc_keysym, &dgen_slot_prev[RCBK] },
722 { "joy_slot_prev", rc_joypad, &dgen_slot_prev[RCBJ] },
723 { "mou_slot_prev", rc_mouse, &dgen_slot_prev[RCBM] },
724 { "key_save", rc_keysym, &dgen_save[RCBK] },
725 { "joy_save", rc_joypad, &dgen_save[RCBJ] },
726 { "mou_save", rc_mouse, &dgen_save[RCBM] },
727 { "key_load", rc_keysym, &dgen_load[RCBK] },
728 { "joy_load", rc_joypad, &dgen_load[RCBJ] },
729 { "mou_load", rc_mouse, &dgen_load[RCBM] },
730 { "key_z80_toggle", rc_keysym, &dgen_z80_toggle[RCBK] },
731 { "joy_z80_toggle", rc_joypad, &dgen_z80_toggle[RCBJ] },
732 { "mou_z80_toggle", rc_mouse, &dgen_z80_toggle[RCBM] },
733 { "key_cpu_toggle", rc_keysym, &dgen_cpu_toggle[RCBK] },
734 { "joy_cpu_toggle", rc_joypad, &dgen_cpu_toggle[RCBJ] },
735 { "mou_cpu_toggle", rc_mouse, &dgen_cpu_toggle[RCBM] },
736 { "key_stop", rc_keysym, &dgen_stop[RCBK] },
737 { "joy_stop", rc_joypad, &dgen_stop[RCBJ] },
738 { "mou_stop", rc_mouse, &dgen_stop[RCBM] },
739 { "key_game_genie", rc_keysym, &dgen_game_genie[RCBK] },
740 { "joy_game_genie", rc_joypad, &dgen_game_genie[RCBJ] },
741 { "mou_game_genie", rc_mouse, &dgen_game_genie[RCBM] },
742 { "key_fullscreen_toggle", rc_keysym, &dgen_fullscreen_toggle[RCBK] },
743 { "joy_fullscreen_toggle", rc_joypad, &dgen_fullscreen_toggle[RCBJ] },
744 { "mou_fullscreen_toggle", rc_mouse, &dgen_fullscreen_toggle[RCBM] },
745 { "key_debug_enter", rc_keysym, &dgen_debug_enter[RCBK] },
746 { "joy_debug_enter", rc_joypad, &dgen_debug_enter[RCBJ] },
747 { "mou_debug_enter", rc_mouse, &dgen_debug_enter[RCBM] },
748 { "key_prompt", rc_keysym, &dgen_prompt[RCBK] },
749 { "joy_prompt", rc_joypad, &dgen_prompt[RCBJ] },
750 { "mou_prompt", rc_mouse, &dgen_prompt[RCBM] },
751 { "bool_vdp_hide_plane_a", rc_boolean, &dgen_vdp_hide_plane_a },
752 { "bool_vdp_hide_plane_b", rc_boolean, &dgen_vdp_hide_plane_b },
753 { "bool_vdp_hide_plane_w", rc_boolean, &dgen_vdp_hide_plane_w },
754 { "bool_vdp_hide_sprites", rc_boolean, &dgen_vdp_hide_sprites },
755 { "bool_vdp_sprites_boxing", rc_boolean, &dgen_vdp_sprites_boxing },
756 { "int_vdp_sprites_boxing_fg", rc_number, &dgen_vdp_sprites_boxing_fg },
757 { "int_vdp_sprites_boxing_bg", rc_number, &dgen_vdp_sprites_boxing_bg },
758 { "bool_autoload", rc_boolean, &dgen_autoload },
759 { "bool_autosave", rc_boolean, &dgen_autosave },
760 { "bool_autoconf", rc_boolean, &dgen_autoconf },
761 { "bool_frameskip", rc_boolean, &dgen_frameskip },
762 { "bool_show_carthead", rc_boolean, &dgen_show_carthead },
763 { "str_rom_path", rc_rom_path,
764 (intptr_t *)((void *)&dgen_rom_path) }, // SH
765 { "bool_raw_screenshots", rc_boolean, &dgen_raw_screenshots },
766 { "ctv_craptv_startup", rc_ctv, &dgen_craptv }, // SH
767 { "scaling_startup", rc_scaling, &dgen_scaling }, // SH
768 { "emu_z80_startup", rc_emu_z80, &dgen_emu_z80 }, // SH
769 { "emu_m68k_startup", rc_emu_m68k, &dgen_emu_m68k }, // SH
770 { "bool_sound", rc_boolean, &dgen_sound }, // SH
771 { "int_soundrate", rc_soundrate, &dgen_soundrate }, // SH
772 { "int_soundsegs", rc_number, &dgen_soundsegs }, // SH
773 { "int_soundsamples", rc_number, &dgen_soundsamples }, // SH
774 { "int_volume", rc_number, &dgen_volume },
775 { "key_volume_inc", rc_keysym, &dgen_volume_inc[RCBK] },
776 { "joy_volume_inc", rc_joypad, &dgen_volume_inc[RCBJ] },
777 { "mou_volume_inc", rc_mouse, &dgen_volume_inc[RCBM] },
778 { "key_volume_dec", rc_keysym, &dgen_volume_dec[RCBK] },
779 { "joy_volume_dec", rc_joypad, &dgen_volume_dec[RCBJ] },
780 { "mou_volume_dec", rc_mouse, &dgen_volume_dec[RCBM] },
781 { "bool_mjazz", rc_boolean, &dgen_mjazz }, // SH
782 { "int_nice", rc_number, &dgen_nice },
783 { "int_hz", rc_number, &dgen_hz }, // SH
784 { "bool_pal", rc_boolean, &dgen_pal }, // SH
785 { "region", rc_region, &dgen_region }, // SH
786 { "str_region_order", rc_string,
787 (intptr_t *)((void *)&dgen_region_order) },
788 { "bool_fps", rc_boolean, &dgen_fps },
789 { "bool_buttons", rc_boolean, &dgen_buttons },
790 { "bool_fullscreen", rc_boolean, &dgen_fullscreen }, // SH
791 { "int_info_height", rc_number, &dgen_info_height }, // SH
792 { "int_width", rc_number, &dgen_width }, // SH
793 { "int_height", rc_number, &dgen_height }, // SH
794 { "int_scale", rc_number, &dgen_scale }, // SH
795 { "int_scale_x", rc_number, &dgen_x_scale }, // SH
796 { "int_scale_y", rc_number, &dgen_y_scale }, // SH
797 { "int_depth", rc_number, &dgen_depth }, // SH
798 { "bool_aspect", rc_boolean, &dgen_aspect }, // SH
799 { "bool_swab", rc_boolean, &dgen_swab }, // SH
800 { "bool_opengl", rc_boolean, &dgen_opengl }, // SH
801 { "bool_opengl_stretch", rc_boolean, &dgen_opengl_stretch }, // SH
802 // deprecated, use bool_aspect
803 { "bool_opengl_aspect", rc_boolean, &dgen_aspect },
804 // deprecated, use int_width
805 { "int_opengl_width", rc_number, &dgen_width },
806 // deprecated, use int_height
807 { "int_opengl_height", rc_number, &dgen_height },
808 { "bool_opengl_linear", rc_boolean, &dgen_opengl_linear }, // SH
809 { "bool_opengl_32bit", rc_boolean, &dgen_opengl_32bit }, // SH
810 // deprecated, use bool_swab
811 { "bool_opengl_swap", rc_boolean, &dgen_swab }, // SH
812 { "bool_opengl_square", rc_boolean, &dgen_opengl_square }, // SH
813 { "bool_doublebuffer", rc_boolean, &dgen_doublebuffer }, // SH
814 { "bool_screen_thread", rc_boolean, &dgen_screen_thread }, // SH
815 { "bool_joystick", rc_boolean, &dgen_joystick }, // SH
816 { "int_mouse_delay", rc_number, &dgen_mouse_delay },
817 { NULL, NULL, NULL }
818 };
819
820 struct rc_binding rc_binding_head = {
821 &rc_binding_head,
822 &rc_binding_head,
823 { { false, RCBK, 0, } },
824 NULL,
825 NULL
826 };
827
rc_binding_cleanup(void)828 static void rc_binding_cleanup(void)
829 {
830 struct rc_binding *rcb = rc_binding_head.next;
831 struct rc_binding *next;
832
833 while (rcb != &rc_binding_head) {
834 next = rcb->next;
835 assert(rcb->to != NULL);
836 assert((intptr_t)rcb->to != -1);
837 free(rcb->to);
838 #ifndef NDEBUG
839 memset(rcb, 0x66, sizeof(*rcb));
840 #endif
841 free(rcb);
842 rcb = next;
843 }
844 }
845
rc_binding_add(const char * rc,const char * to)846 struct rc_field *rc_binding_add(const char *rc, const char *to)
847 {
848 static bool registered = false;
849 size_t rc_sz = (strlen(rc) + 1);
850 struct rc_field *rcf = rc_fields;
851 struct rc_binding *rcb;
852 struct rc_binding_item item[elemof(rcb->item)];
853 unsigned int i;
854 const char *s;
855 size_t off;
856 char *new_to;
857
858 if ((registered == false) && (atexit(rc_binding_cleanup) == 0))
859 registered = true;
860 // Check if the RC name looks like a valid binding.
861 for (off = 0; (rc[off] != '\0'); ++off) {
862 if (RC_BIND_PREFIX[off] == '\0')
863 break;
864 if (rc[off] != RC_BIND_PREFIX[off])
865 return NULL;
866 }
867 if (rc[off] == '\0')
868 return NULL;
869 // Extract multiple keysyms or joypad codes from RC name.
870 memset(item, 0, sizeof(item));
871 s = &rc[off];
872 i = 0;
873 while (s += strspn(s, " \t\n"), off = strcspn(s, " \t\n")) {
874 char tmp[64];
875 intptr_t code;
876 enum rc_binding_type type;
877
878 if (i == elemof(item))
879 return NULL;
880 snprintf(tmp, sizeof(tmp), "%.*s", (int)off, s);
881 if ((type = RCBJ, ((code = rc_joypad(tmp, NULL)) == -1)) &&
882 (type = RCBK, ((code = rc_keysym(tmp, NULL)) == -1)) &&
883 (type = RCBM, ((code = rc_mouse(tmp, NULL)) == -1)))
884 return NULL;
885 item[i].assigned = true;
886 item[i].type = type;
887 item[i].code = code;
888 ++i;
889 s += off;
890 }
891 // Find a free entry in rc_fields[].
892 while (rcf->fieldname != NULL)
893 ++rcf;
894 if ((rcf - rc_fields) == (elemof(rc_fields) - 1))
895 return NULL;
896 assert(rcf->parser == NULL);
897 assert(rcf->variable == NULL);
898 // Allocate binding.
899 if ((rcb = (struct rc_binding *)malloc(sizeof(*rcb) + rc_sz)) == NULL)
900 return NULL;
901 if ((new_to = strdup(to)) == NULL) {
902 free(new_to);
903 free(rcb);
904 return NULL;
905 }
906 // -1 is reserved, thus invalid. Should not happen anyway.
907 if ((intptr_t)new_to == -1)
908 abort();
909 // Configure binding.
910 rcb->prev = rc_binding_head.prev;
911 rcb->next = &rc_binding_head;
912 rcb->prev->next = rcb;
913 rcb->next->prev = rcb;
914 memcpy(rcb->item, item, sizeof(rcb->item));
915 rcb->rc = ((char *)rcb + sizeof(*rcb));
916 rcb->to = new_to;
917 memcpy(rcb->rc, rc, rc_sz);
918 // Configure RC field.
919 rcf->fieldname = rcb->rc;
920 rcf->parser = rc_bind;
921 rcf->variable = (intptr_t *)((void *)&rcb->to);
922 return rcf;
923 }
924
rc_binding_del(rc_field * rcf)925 void rc_binding_del(rc_field *rcf)
926 {
927 struct rc_binding *rcb =
928 containerof(rcf->variable, struct rc_binding, to);
929
930 assert(rcf >= &rc_fields[0]);
931 assert(rcf < &rc_fields[elemof(rc_fields)]);
932 assert(rcf->fieldname != NULL);
933 assert(rcf->parser != NULL);
934 assert(rcf->variable != NULL);
935 if (rcf->parser != rc_bind)
936 return;
937 assert(rcb != &rc_binding_head);
938 // Clean-up.
939 rcb->prev->next = rcb->next;
940 rcb->next->prev = rcb->prev;
941 assert(rcb->to != NULL);
942 assert((intptr_t)rcb->to != -1);
943 free(rcb->to);
944 #ifndef NDEBUG
945 memset(rcb, 0x88, (sizeof(*rcb) + strlen(rcb->rc) + 1));
946 #endif
947 free(rcb);
948 // Shift the next entries.
949 do {
950 memcpy(rcf, (rcf + 1), sizeof(*rcf));
951 ++rcf;
952 }
953 while (rcf->fieldname != NULL);
954 assert(rcf < &rc_fields[elemof(rc_fields)]);
955 }
956
rc_bind(const char * value,intptr_t * variable)957 intptr_t rc_bind(const char *value, intptr_t *variable)
958 {
959 struct rc_binding *rcb = containerof(variable, struct rc_binding, to);
960 char *to;
961
962 assert(*variable != -1);
963 assert(rcb != NULL);
964 assert(rcb->prev != NULL);
965 assert(rcb->next != NULL);
966 assert(rcb->rc != NULL);
967 assert(rcb->to != NULL);
968 assert((intptr_t)rcb->to != -1);
969 if ((to = strdup(value)) == NULL) {
970 free(to);
971 // Get the previous value.
972 to = rcb->to;
973 }
974 // -1 is reserved, thus invalid. Should not happen anyway.
975 else if ((intptr_t)to == -1)
976 abort();
977 else
978 free(rcb->to);
979 rcb->to = NULL; // Will be updated by the return value.
980 // This function must always return a valid pointer.
981 return (intptr_t)to;
982 }
983
984 /* Replace unprintable characters */
strclean(char * s)985 static char *strclean(char *s)
986 {
987 size_t i;
988
989 for (i = 0; (s[i] != '\0'); ++i)
990 if (!isprint(s[i]))
991 s[i] = '?';
992 return s;
993 }
994
995 /* This is for cleaning up rc_str fields at exit */
996 struct rc_str *rc_str_list = NULL;
997
rc_str_cleanup(void)998 void rc_str_cleanup(void)
999 {
1000 struct rc_str *rs = rc_str_list;
1001
1002 while (rs != NULL) {
1003 if (rs->val == rs->alloc)
1004 rs->val = NULL;
1005 free(rs->alloc);
1006 rs->alloc = NULL;
1007 rs = rs->next;
1008 }
1009 }
1010
1011 /* Parse the rc file */
parse_rc(FILE * file,const char * name)1012 void parse_rc(FILE *file, const char *name)
1013 {
1014 struct rc_field *rc_field = NULL;
1015 intptr_t potential;
1016 int overflow = 0;
1017 size_t len;
1018 size_t parse;
1019 ckvp_t ckvp = CKVP_INIT;
1020 char buf[1024];
1021
1022 if ((file == NULL) || (name == NULL))
1023 return;
1024 read:
1025 len = fread(buf, 1, sizeof(buf), file);
1026 /* Check for read errors first */
1027 if ((len == 0) && (ferror(file))) {
1028 fprintf(stderr, "rc: %s: %s\n", name, strerror(errno));
1029 return;
1030 }
1031 /* The goal is to make an extra pass with len == 0 when feof(file) */
1032 parse = 0;
1033 parse:
1034 parse += ckvp_parse(&ckvp, (len - parse), &(buf[parse]));
1035 switch (ckvp.state) {
1036 case CKVP_NONE:
1037 /* Nothing to do */
1038 break;
1039 case CKVP_OUT_FULL:
1040 /*
1041 Buffer is full, field is probably too large. We don't want
1042 to report it more than once, so just store a flag for now.
1043 */
1044 overflow = 1;
1045 break;
1046 case CKVP_OUT_KEY:
1047 /* Got a key */
1048 if (overflow) {
1049 fprintf(stderr, "rc: %s:%u:%u: key field too large\n",
1050 name, ckvp.line, ckvp.column);
1051 rc_field = NULL;
1052 overflow = 0;
1053 break;
1054 }
1055 /* Find the related rc_field in rc_fields */
1056 assert(ckvp.out_size < sizeof(ckvp.out));
1057 ckvp.out[(ckvp.out_size)] = '\0';
1058 for (rc_field = rc_fields; (rc_field->fieldname != NULL);
1059 ++rc_field)
1060 if (!strcasecmp(rc_field->fieldname, ckvp.out))
1061 goto key_over;
1062 /* Try to add it as a new binding. */
1063 if ((rc_field = rc_binding_add(ckvp.out, "")) != NULL)
1064 goto key_over;
1065 fprintf(stderr, "rc: %s:%u:%u: unknown key `%s'\n",
1066 name, ckvp.line, ckvp.column, strclean(ckvp.out));
1067 key_over:
1068 break;
1069 case CKVP_OUT_VALUE:
1070 /* Got a value */
1071 if (overflow) {
1072 fprintf(stderr, "rc: %s:%u:%u: value field too large\n",
1073 name, ckvp.line, ckvp.column);
1074 overflow = 0;
1075 break;
1076 }
1077 assert(ckvp.out_size < sizeof(ckvp.out));
1078 ckvp.out[(ckvp.out_size)] = '\0';
1079 if ((rc_field == NULL) || (rc_field->fieldname == NULL))
1080 break;
1081 potential = rc_field->parser(ckvp.out, rc_field->variable);
1082 /* If we got a bad value, discard and warn user */
1083 if ((rc_field->parser != rc_number) && (potential == -1))
1084 fprintf(stderr,
1085 "rc: %s:%u:%u: invalid value for key"
1086 " `%s': `%s'\n",
1087 name, ckvp.line, ckvp.column,
1088 rc_field->fieldname, strclean(ckvp.out));
1089 else if ((rc_field->parser == rc_string) ||
1090 (rc_field->parser == rc_rom_path)) {
1091 struct rc_str *rs =
1092 (struct rc_str *)rc_field->variable;
1093
1094 if (rc_str_list == NULL) {
1095 atexit(rc_str_cleanup);
1096 rc_str_list = rs;
1097 }
1098 else if (rs->alloc == NULL) {
1099 rs->next = rc_str_list;
1100 rc_str_list = rs;
1101 }
1102 else
1103 free(rs->alloc);
1104 rs->alloc = (char *)potential;
1105 rs->val = rs->alloc;
1106 }
1107 else if ((rc_field->parser == rc_region) &&
1108 (rc_field->variable == &dgen_region)) {
1109 /*
1110 Another special case: updating region also updates
1111 PAL and Hz settings.
1112 */
1113 *(rc_field->variable) = potential;
1114 if (*(rc_field->variable)) {
1115 int hz;
1116 int pal;
1117
1118 md::region_info(dgen_region, &pal, &hz,
1119 0, 0, 0);
1120 dgen_hz = hz;
1121 dgen_pal = pal;
1122 }
1123 }
1124 else
1125 *(rc_field->variable) = potential;
1126 break;
1127 case CKVP_ERROR:
1128 default:
1129 fprintf(stderr, "rc: %s:%u:%u: syntax error, aborting\n",
1130 name, ckvp.line, ckvp.column);
1131 return;
1132 }
1133 /* Not done with the current buffer? */
1134 if (parse != len)
1135 goto parse;
1136 /* If len != 0, try to read once again */
1137 if (len != 0)
1138 goto read;
1139 }
1140
1141 /* Dump the rc file */
dump_rc(FILE * file)1142 void dump_rc(FILE *file)
1143 {
1144 const struct rc_field *rc = rc_fields;
1145
1146 while (rc->fieldname != NULL) {
1147 intptr_t val = *rc->variable;
1148 char *s = backslashify((const uint8_t *)rc->fieldname,
1149 strlen(rc->fieldname), 0, NULL);
1150
1151 if (s == NULL) {
1152 ++rc;
1153 continue;
1154 }
1155 fprintf(file, "%s = ", s);
1156 free(s);
1157 if ((rc->parser == rc_number) ||
1158 (rc->parser == rc_soundrate))
1159 fprintf(file, "%ld", (long)val);
1160 else if (rc->parser == rc_keysym) {
1161 char *ks = dump_keysym(val);
1162
1163 if (ks != NULL) {
1164 fprintf(file, "\"%s\"", ks);
1165 free(ks);
1166 }
1167 else
1168 fputs("''", file);
1169 }
1170 else if (rc->parser == rc_boolean)
1171 fprintf(file, "%s", ((val) ? "true" : "false"));
1172 else if (rc->parser == rc_joypad) {
1173 char *js = dump_joypad(val);
1174
1175 if (js != NULL) {
1176 fprintf(file, "\"%s\"", js);
1177 free(js);
1178 }
1179 else
1180 fputs("''", file);
1181 }
1182 else if (rc->parser == rc_mouse) {
1183 char *mo = dump_mouse(val);
1184
1185 if (mo != NULL) {
1186 fprintf(file, "\"%s\"", mo);
1187 free(mo);
1188 }
1189 else
1190 fputs("''", file);
1191 }
1192 else if (rc->parser == rc_ctv)
1193 fprintf(file, "%s", ctv_names[val]);
1194 else if (rc->parser == rc_scaling)
1195 fprintf(file, "\"%s\"", scaling_names[val]);
1196 else if (rc->parser == rc_emu_z80)
1197 fprintf(file, "%s", emu_z80_names[val]);
1198 else if (rc->parser == rc_emu_m68k)
1199 fprintf(file, "%s", emu_m68k_names[val]);
1200 else if (rc->parser == rc_region) {
1201 if (isgraph((char)val))
1202 fputc((char)val, file);
1203 else
1204 fputs("' '", file);
1205 }
1206 else if ((rc->parser == rc_string) ||
1207 (rc->parser == rc_rom_path)) {
1208 struct rc_str *rs = (struct rc_str *)rc->variable;
1209
1210 if ((rs->val == NULL) ||
1211 ((s = backslashify
1212 ((const uint8_t *)rs->val,
1213 strlen(rs->val), 0, NULL)) == NULL))
1214 fprintf(file, "\"\"");
1215 else {
1216 fprintf(file, "\"%s\"", s);
1217 free(s);
1218 }
1219 }
1220 else if (rc->parser == rc_bind) {
1221 s = *(char **)rc->variable;
1222 assert(s != NULL);
1223 assert((intptr_t)s != -1);
1224 s = backslashify((uint8_t *)s, strlen(s), 0, NULL);
1225 fputc('"', file);
1226 if (s != NULL) {
1227 fputs(s, file);
1228 free(s);
1229 }
1230 fputc('"', file);
1231 }
1232 fputs("\n", file);
1233 ++rc;
1234 }
1235 }
1236