Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  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. */
  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. */
  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). */
  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. */
  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.  */
  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. */
  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.  */
  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.  */
  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.  
  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 */
  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.  
  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.  
  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.  
  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.  
  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.  
  564. intptr_t rc_number(const char *value, intptr_t *)
  565. {
  566.         return strtol(value, NULL, 0);
  567. }
  568.  
  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.  
  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.  
  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.  
  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.  
  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 */
  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.  
  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 */
  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 */
  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.