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