#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "../../fceu.h"
#include "../../fceu-types.h"
#include "../../vsuni.h"
#include "../../git.h"
#include "../../driver.h"
#include "../../cart.h"

#include "libretro_dipswitch.h"

#define MAX_CORE_OPTIONS 8
#define MAX_VALUES 10

extern CartInfo iNESCart;

typedef struct {
   const char  *name;
   uint8       value;
} SETTING;

typedef struct {
   const char  *option_name;
   SETTING     settings[MAX_VALUES];
} DIPSWITCH;

typedef struct {
   unsigned    game_id;
   const char  *game_name;
   DIPSWITCH   core_options[MAX_CORE_OPTIONS];
} VSUNIGAME;

static VSUNIGAME dipswitch_battlecity = {
   VS_BATTLECITY, "battlecity",
   {
      {  "Credits for 2 Players",
         {
            { "2", 0x01 },
            { "1", 0x00 },
            { NULL, 0 },
         },
      },
      {  "Lives",
         {
            { "5", 0x02 },
            { "3", 0x00 },
            { NULL, 0 },
         },
      },
      {  "Demo Sounds",
         {
            { "disabled", 0x04 },
            { "enabled",  0x00 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   },
};

static VSUNIGAME dipswitch_castlevania = {
   VS_CASTLEVANIA, "castlevania",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "2 Coins 1 Credit", 0x06 },
            { "3 Coins 1 Credit", 0x01 },
            { "4 Coins 1 Credit", 0x05 },
            { "5 Coins 1 Credit", 0x03 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Lives",
         {
            { "3", 0x00 },
            { "2", 0x08 },
            { NULL, 0 },
         }
      },
      {  "Bonus",
         {
            { "100k", 0x00 },
            { "200k", 0x20 },
            { "300k", 0x10 },
            { "400k", 0x30 },
            { NULL, 0 },
         },
      },
      {  "Difficulty",
         {
            { "Normal", 0x00 },
            { "Hard",   0x40 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_clucluland = {
   VS_CLUCLULAND, "clucluland",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "1 Coin 4 Credits", 0x06 },
            { "2 Coins 1 Credit", 0x01 },
            { "3 Coins 1 Credit", 0x05 },
            { "4 Coins 1 Credit", 0x03 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Lives",
         {
            { "3", 0x00 },
            { "4", 0x40 },
            { "5", 0x20 },
            { "2", 0x60 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_drmario = {
   VS_DRMARIO, "drmario",
   {
      {  "Drop Rate Increase After",
         {
            { "7 Pills",  0x00 },
            { "8 Pills",  0x01 },
            { "9 Pills",  0x02 },
            { "10 Pills", 0x03 },
            { NULL, 0 },
         },
      },
      {  "Virus Level",
         {
            { "1", 0x00 },
            { "3", 0x04 },
            { "5", 0x08 },
            { "7", 0x0c },
            { NULL, 0 },
         },
      },
      {  "Drop Speed Up",
         {
            { "Slow",    0x00 },
            { "Medium",  0x10 },
            { "Fast",    0x20 },
            { "Fastest", 0x30 },
            { NULL, 0 },
         },
      },
      {  "Free Play",
         {
            { "disabled", 0x00 },
            { "enabled",  0x40 },
            { NULL, 0 },
         },
      },
      {  "Demo Sounds",
         {
            { "enabled",  0x80 },
            { "disabled", 0x00 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_duckhunt = {
   VS_DUCKHUNT, "duckhunt",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "2 Coins 1 Credit", 0x06 },
            { "3 Coins 1 Credit", 0x01 },
            { "4 Coins 1 Credit", 0x05 },
            { "5 Coins 1 Credit", 0x03 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Difficulty",
         {
            { "Normal", 0x08 },
            { "Medium", 0x10 },
            { "Hard",   0x18 },
            { "Easy",   0x00 },
            { NULL, 0 },
         },
      },
      {  "Miss per game",
         {
            { "5", 0x20 },
            { "3", 0x00 },
            { NULL, 0 },
         },
      },
      {  "Bonus Life",
         {
            { "30000",  0x00 },
            { "50000",  0x40 },
            { "80000",  0x80 },
            { "100000", 0xc0 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_excitebike = {
   VS_EXITEBIKE, "excitebike",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "1 Coin 4 Credits", 0x06 },
            { "2 Coins 1 Credit", 0x01 },
            { "3 Coins 1 Credit", 0x05 },
            { "4 Coins 1 Credit", 0x03 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Bonus Bike",
         {
            { "100k and Every 50k", 0x00 },
            { "100k Only",          0x08 },
            { "Every 100k",         0x10 },
            { "None",               0x18 },
            { NULL, 0 },
         },
      },
      {  "1st Qualifying Time",
         {
            { "Easy", 0x00 },
            { "Hard", 0x20 },
            { NULL, 0 },
         },
      },
      {  "2nd Qualifying Time",
         {
            { "Easy", 0x00 },
            { "Hard", 0x40 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_freedomforce = {
   VS_FREEDOMFORCE, "freedomforce",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "2 Coins 1 Credit", 0x06 },
            { "3 Coins 1 Credit", 0x01 },
            { "4 Coins 1 Credit", 0x05 },
            { "5 Coins 1 Credit", 0x03 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Health Awarded At",
         {
            { "10k 50k",           0x10 },
            { "20k 60k",           0x00 },
            { "20k 60k Every 60k", 0x08 },
            { "10k 50k Every 50k", 0x18 },
            { NULL, 0 },
         },
      },
      {  "Difficulty (Damage)",
         {
            { "Normal",  0x00 },
            { "Easy",    0x40 },
            { "Hard",    0x20 },
            { "Hardest", 0x60 },
            { NULL, 0 },
         },
      },
      {  "Difficulty (Enemy)",
         {
            { "Normal", 0x80 },
            { "Hard",   0x00 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_goonies = {
   VS_GOONIES, "goonies",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "2 Coins 1 Credit", 0x06 },
            { "3 Coins 1 Credit", 0x01 },
            { "4 Coins 1 Credit", 0x05 },
            { "5 Coins 1 Credit", 0x03 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Lives",
         {
            { "3", 0x08 },
            { "4", 0x00 },
            { NULL, 0 },
         },
      },
      {  "Bonus",
         {
            { "40000", 0x00 },
            { "50000", 0x20 },
            { "60000", 0x10 },
            { "70000", 0x30 },
            { NULL, 0 },
         },
      },
      {  "Timer",
         {
            { "Slow", 0x00 },
            { "Fast", 0x40 },
            { NULL, 0 },
         },
      },
      { "Demo Sounds",
         {
            { "enabled",  0x80 },
            { "disabled", 0x00 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_gradius = {
   VS_GRADIUS, "gradius",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "2 Coins 1 Credit", 0x06 },
            { "3 Coins 1 Credit", 0x01 },
            { "4 Coins 1 Credit", 0x05 },
            { "5 Coins 1 Credit", 0x03 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Lives",
         {
            { "3", 0x08 },
            { "4", 0x00 },
            { NULL, 0 },
         },
      },
      {  "Bonus",
         {
            { "100k", 0x00 },
            { "200k", 0x20 },
            { "300k", 0x10 },
            { "400k", 0x30 },
            { NULL, 0 },
         },
      },
      {  "Difficulty",
         {
            { "Normal", 0x00 },
            { "Hard",   0x40 },
            { NULL, 0 },
         },
      },
      { "Demo Sounds",
         {
            { "enabled",  0x80 },
            { "disabled", 0x00 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_gumshoe = {
   VS_GUMSHOE, "gumshoe",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "2 Coins 1 Credit", 0x06 },
            { "3 Coins 1 Credit", 0x01 },
            { "4 Coins 1 Credit", 0x05 },
            { "5 Coins 1 Credit", 0x03 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Difficulty",
         {
            { "Normal",  0x08 },
            { "Easy",    0x00 },
            { "Hard",    0x10 },
            { "Hardest", 0x18 },
            { NULL, 0 },
         },
      },
      {  "Lives",
         {
            { "3", 0x20 },
            { "5", 0x00 },
            { NULL, 0 },
         },
      },
      {  "Bullets per Balloon",
         {
            { "3", 0x00 },
            { "2", 0x40 },
            { NULL, 0 },
         },
      },
      {  "Bonus Man Awarded",
         {
            { "At 50000", 0x00 },
            { "None",     0x80 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_hogansalley = {
   VS_HOGANSALLEY, "hogansalley",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "2 Coins 1 Credit", 0x06 },
            { "3 Coins 1 Credit", 0x01 },
            { "4 Coins 1 Credit", 0x05 },
            { "5 Coins 1 Credit", 0x03 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Difficulty",
         {
            { "Normal",  0x08 },
            { "Easy",    0x00 },
            { "Hard",    0x10 },
            { "Hardest", 0x18 },
            { NULL, 0 },
         },
      },
      {  "Misses Per Game",
         {
            { "5", 0x20 },
            { "3", 0x00 },
            { NULL, 0 },
         },
      },
      {  "Bonus Life",
         {
            { "30000",  0x00 },
            { "50000",  0x40 },
            { "80000",  0x80 },
            { "100000", 0xc0 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_iceclimber = {
   VS_ICECLIMBER, "iceclimber",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "1 Coin 4 Credits", 0x06 },
            { "2 Coins 1 Credit", 0x01 },
            { "3 Coins 1 Credit", 0x05 },
            { "4 Coins 1 Credit", 0x03 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Lives",
         {
            { "3", 0x00 },
            { "4", 0x10 },
            { "5", 0x08 },
            { "7", 0x18 },
            { NULL, 0 },
         },
      },
      {  "Difficulty",
         {
            { "Easy", 0x00 },
            { "Hard", 0x20 },
            { NULL, 0 },
         },
      },
      {  "Time before bear appears",
         {
            { "Long",  0x00 },
            { "Short", 0x40 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_jajamaru = {
   VS_JAJAMARU, "jajamaru",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "1 Coin 4 Credits", 0x06 },
            { "2 Coins 1 Credit", 0x01 },
            { "3 Coins 1 Credit", 0x05 },
            { "4 Coins 1 Credit", 0x03 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Lives",
         {
            { "3", 0x00 },
            { "4", 0x10 },
            { "5", 0x08 },
            { NULL, 0 },
         },
      },
      {  "Demo Sounds",
         {
            { "enabled",  0x80 },
            { "disabled", 0x00 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_machrider = {
   VS_MACHRIDER, "machrider",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "1 Coin 4 Credits", 0x06 },
            { "2 Coins 1 Credit", 0x01 },
            { "3 Coins 1 Credit", 0x05 },
            { "4 Coins 1 Credit", 0x03 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Time",
         {
            { "280 (Easy)", 0x00 },
            { "250",        0x10 },
            { "220",        0x08 },
            { "200 (Hard)", 0x18 },
            { NULL, 0 },
         },
      },
      {  "Enemies",
         {
            { "Less", 0x00 },
            { "More", 0x40 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_mightybj = {
   VS_MIGHTYBOMBJACK, "mightybj",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "1 Coin 4 Credits", 0x06 },
            { "2 Coins 1 Credit", 0x01 },
            { "3 Coins 1 Credit", 0x05 },
            { "4 Coins 1 Credit", 0x03 },
            { "5 Coins 1 Credit", 0x07 },
            { NULL, 0 },
         },
      },
      {  "Lives",
         {
            { "3", 0x00 },
            { "4", 0x08 },
            { "5", 0x18 },
            { "2", 0x10 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_pinball = {
   VS_PINBALL, "pinball",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x01 },
            { "1 Coin 2 Credits", 0x06 },
            { "1 Coin 3 Credits", 0x02 },
            { "1 Coin 4 Credits", 0x04 },
            { "2 Coins 1 Credit", 0x05 },
            { "3 Coins 1 Credit", 0x03 },
            { "4 Coins 1 Credit", 0x07 },
            { "Free Play",        0x00 },
            { NULL, 0 },
         },
      },
      {  "Side Drain Walls",
         {
            { "High", 0x00 },
            { "Low",  0x08 },
            { NULL, 0 },
         },
      },
      {  "Bonus Life",
         {
            { "50000", 0x00 },
            { "70000", 0x10 },
            { NULL, 0 },
         },
      },
      {  "Balls",
         {
            { "3", 0x00 },
            { "4", 0x40 },
            { "5", 0x20 },
            { "2", 0x60 },
            { NULL, 0 },
         }
      },
      {  "Ball Speed",
         {
            { "Slow", 0x00 },
            { "Fast", 0x80 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_platoon = {
   VS_PLATOON, "platoon",
   {
      {  "Demo Sounds",
         {
            { "enabled",  0x00 },
            { "disabled", 0x04 },
            { NULL, 0 },
         },
      },
      {  "Difficulty",
         {
            { "Normal", 0x08 },
            { "Medium", 0x10 },
            { "Hard",   0x18 },
            { "Easy",   0x00 },
            { NULL, 0 },
         }
      },
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x20 },
            { "1 Coin 3 Credits", 0x40 },
            { "2 Coins 1 Credit", 0x60 },
            { "3 Coins 1 Credit", 0x80 },
            { "4 Coins 1 Credit", 0xa0 },
            { "5 Coins 1 Credit", 0xc0 },
            { "Free Play",  0xe0 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_rbibaseball = {
   VS_RBIBASEBALL, "atari_rbibb",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x01 },
            { "2 Coins 1 Credit", 0x02 },
            { "3 Coins 1 Credit", 0x03 },
            { NULL, 0 },
         },
      },
      {  "1p-inn 2p-inn time-min",
         {
            { "2 2 4", 0x0c },
            { "3 2 6", 0x00 },
            { "4 3 7", 0x08 },
            { "2 1 3", 0x04 },
            { NULL, 0 },
         }
      },
      {  "Demo Sounds",
         {
            { "enabled",  0x00 },
            { "disabled", 0x10 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_slalom = {
   VS_SLALOM, "slalom",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "2 Coins 1 Credit", 0x06 },
            { "3 Coins 1 Credit", 0x01 },
            { "4 Coins 1 Credit", 0x05 },
            { "5 Coins 1 Credit", 0x03 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Freestyle Points",
         {
            { "Hold Time",  0x08 },
            { "Left Right", 0x00 },
            { NULL, 0 },
         },
      },
      {  "Difficulty",
         {
            { "Normal", 0x10 },
            { "Medium", 0x20 },
            { "Hard",   0x30 },
            { "Easy",   0x00 },
            { NULL, 0 },
         },
      },
      {  "Allow Continue",
         {
            { "enabled",  0x00 },
            { "disabled", 0x40 },
            { NULL, 0 },
         },
      },
      {  "Inverted Input",
         {
            { "disabled", 0x00 },
            { "enabled",  0x80 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_smb = {
   VS_SMB, "smb",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x06 },
            { "1 Coin 3 Credits", 0x01 },
            { "1 Coin 4 Credits", 0x05 },
            { "1 Coin 5 Credits", 0x03 },
            { "2 Coins 1 Credit", 0x04 },
            { "3 Coins 1 Credit", 0x02 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Lives",
         {
            { "3", 0x00 },
            { "2", 0x08 },
            { NULL, 0 },
         },
      },
      {  "Bonus Life",
         {
            { "100 coins", 0x00 },
            { "150 coins", 0x20 },
            { "200 coins", 0x10 },
            { "250 coins", 0x30 },
            { NULL, 0 },
         },
      },
      {  "Timer",
         {
            { "Slow", 0x00 },
            { "Fast", 0x40 },
            { NULL, 0 },
         },
      },
      {  "Continue Lives",
         {
            { "3", 0x80 },
            { "4", 0x00 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_soccer = {
   VS_SOCCER, "soccer",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "1 Coin 4 Credits", 0x06 },
            { "2 Coins 1 Credit", 0x01 },
            { "3 Coins 1 Credit", 0x05 },
            { "4 Coins 1 Credit", 0x03 },
            { "Free Play",        0x07 },
            { NULL, 0 },
         },
      },
      {  "Points Timer",
         {
            { "1000 Pts", 0x08 },
            { "1200 Pts", 0x18 },
            { "600 Pts",  0x00 },
            { "800 Pts",  0x10 },
            { NULL, 0 },
         },
      },
      { "Difficulty",
         {
            { "Normal", 0x40 },
            { "Medium", 0x20 },
            { "Hard",   0x60 },
            { "Easy",   0x00 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_starluster = {
   VS_STARLUSTER, "starluster",
   {
      { "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x02 },
            { "2 Coins 1 Credit", 0x01 },
            { "3 Coins 1 Credit", 0x03 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_superskykid = {
   VS_SUPERSKYKID, "superskykid",
   {
      {  "Difficulty",
         {

            { "Normal", 0x01 },
            { "Medium", 0x02 },
            { "Hard",   0x03 },
            { "Easy",   0x00 },
            { NULL, 0 },
         }
      },
      {  "Lives",
         {
            { "3", 0x04 },
            { "2", 0x00 },
            { NULL, 0 },
         },
      },
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x08 },
            { "2 Coins 1 Credit", 0x10 },
            { "3 Coins 1 Credit", 0x18 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_superxevious = {
   VS_SUPERXEVIOUS, "superxevious",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x10 },
            { "2 Coins 1 Credit", 0x20 },
            { "3 Coins 1 Credit", 0x30 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_tetris = {
   VS_TETRIS, "tetris",
   {
      {  "Difficulty",
         {
            { "Normal", 0x04 },
            { "Medium", 0x08 },
            { "Hard",   0x0c },
            { "Easy",   0x00 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_tkoboxing = {
   VS_TKOBOXING, "tkoboxing",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x01 },
            { "2 Coins 1 Credit", 0x02 },
            { "3 Coins 1 Credit", 0x03 },
            { NULL, 0 },
         },
      },
      {  "Difficulty",
         {
            { "Normal",    0x04 },
            { "Hard",      0x08 },
            { "Very Hard", 0x0c },
            { "Easy",      0x00 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

static VSUNIGAME dipswitch_topgun = {
   VS_TOPGUN, "topgun",
   {
      {  "Coinage",
         {
            { "1 Coin 1 Credit",  0x00 },
            { "1 Coin 2 Credits", 0x04 },
            { "1 Coin 3 Credits", 0x02 },
            { "2 Coins 1 Credit", 0x01 },
            { "3 Coins 1 Credit", 0x05 },
            { "4 Coins 1 Credit", 0x06 },
            { "5 Coins 1 Credit", 0x03 },
            { "Free Play",  0x07 },
            { NULL, 0 },
         },
      },
      {  "Lives per Coin",
         {
            { "3-12 Max", 0x00 },
            { "2-9 Max",  0x08 },
            { NULL, 0 },
         },
      },
      {  "Bonus Life",
         {
            { "30k and Every 50k",   0x00 },
            { "50k and Every 100k",  0x20 },
            { "100k and Every 150k", 0x10 },
            { "200k and Every 200k", 0x30 },
            { NULL, 0 },
         },
      },
      {  "Difficulty",
         {
            { "Easy", 0x00 },
            { "Hard", 0x40 },
            { NULL, 0 },
         },
      },
      {  "Demo Sounds",
         {
            { "enabled",  0x80 },
            { "disabled", 0x00 },
            { NULL, 0 },
         },
      },
      {  NULL, { {0} } },
   }
};

enum {
   DPSW_NONE = 0,
   DPSW_VSUNI,
   DPSW_NWC,
};

static struct retro_core_option_v2_definition vscoreopt[MAX_CORE_OPTIONS];
static VSUNIGAME *vsgame = NULL;

static char *core_key[MAX_CORE_OPTIONS];
static unsigned dipswitch_type = DPSW_NONE;
static unsigned numCoreOptions = 0;
static unsigned numValues[MAX_VALUES] = {0};
static uint8 dipswitchPreset = 0;

static const char *str_to_corekey(char *s)
{
   char *str = s;
   unsigned i = 0;

   while (str[i] != '\0')
   {
      if (str[i] == ' ')
         str[i] = '_';
      i++;
   }
   str[i] = '\0';

   return str;
}

static void make_core_options(struct retro_core_option_v2_definition *vs_core_options)
{
   unsigned i, j;

   for (i = 0; i < numCoreOptions; i++)
   {
      const char *game_name   = vsgame->game_name;
      const char *option_name = vsgame->core_options[i].option_name;
      char key[100]           = {0};

      memset(&vs_core_options[i], 0,
            sizeof(struct retro_core_option_v2_definition));

      /* Set core key and sanitize string */
      sprintf(key, "fceumm_dipswitch_%s-%s", game_name, option_name);
      core_key[i] = calloc(strlen(key) + 1, sizeof(char));
      strcpy(core_key[i], key);
      vs_core_options[i].key = str_to_corekey(core_key[i]);

      /* Set desc */
      vs_core_options[i].desc = option_name;

      /* Set category_key */
      vs_core_options[i].category_key = "dip_switch";

      /* Set core values */
      for (j = 0; j < numValues[i]; j++) {
         const char *var_value = vsgame->core_options[i].settings[j].name;
         vs_core_options[i].values[j].value = var_value;
      }

      /* Set default value. Top entry used as default */
      vs_core_options[i].default_value = vsgame->core_options[i].settings[0].name;
   }
}

static VSUNIGAME *get_vsuni_dipswitch(unsigned id)
{
   switch (id) {
   case VS_BATTLECITY:
      return (&dipswitch_battlecity);
   case VS_CASTLEVANIA:
      return (&dipswitch_castlevania);
   case VS_CLUCLULAND:
      return (&dipswitch_clucluland);
   case VS_DRMARIO:
      return (&dipswitch_drmario);
   case VS_DUCKHUNT:
      return (&dipswitch_duckhunt);
   case VS_EXITEBIKE:
      return (&dipswitch_excitebike);
   case VS_FREEDOMFORCE:
      return (&dipswitch_freedomforce);
   case VS_GOONIES:
      return (&dipswitch_goonies);
   case VS_GRADIUS:
      return (&dipswitch_gradius);
   case VS_GUMSHOE:
      return (&dipswitch_gumshoe);
   case VS_HOGANSALLEY:
      return (&dipswitch_hogansalley);
   case VS_ICECLIMBER:
      return (&dipswitch_iceclimber);
   case VS_JAJAMARU:
      return (&dipswitch_jajamaru);
   /* case VS_LADIESGOLF: */
   /* case VS_STROKEANDMATCHGOLF: */
   case VS_MACHRIDER:
      return (&dipswitch_machrider);
   case VS_MIGHTYBOMBJACK:
      return (&dipswitch_mightybj);
   case VS_PINBALL:
      return (&dipswitch_pinball);
   case VS_PLATOON:
      return (&dipswitch_platoon);
   case VS_RBIBASEBALL:
      return (&dipswitch_rbibaseball);
   case VS_SLALOM:
      return (&dipswitch_slalom);
   case VS_SMB:
      return (&dipswitch_smb);
   case VS_SOCCER:
      return (&dipswitch_soccer);
   case VS_STARLUSTER:
      return (&dipswitch_starluster);
   case VS_SUPERSKYKID:
      return (&dipswitch_superskykid);
   case VS_SUPERXEVIOUS:
      return (&dipswitch_superxevious);
   case VS_TETRIS:
      return (&dipswitch_tetris);
   case VS_TKOBOXING:
      return (&dipswitch_tkoboxing);
   case VS_TOPGUN:
      return (&dipswitch_topgun);
   }

   FCEU_printf("No dipsettings found. %d\n", id);
   return (NULL);
}

static void update_dipswitch_vsuni(void)
{
   unsigned index_key;
   uint8 vsdip_new = 0;
   uint8 last_vsdip = FCEUI_VSUniGetDIPs();

   for (index_key = 0; index_key < numCoreOptions; index_key++)
   {
      struct retro_variable var = { NULL, NULL };
      const char *key = vscoreopt[index_key].key;
      unsigned index_value;

      var.key = key;
      if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) == 0)
         continue;

      for (index_value = 0; index_value < numValues[index_key]; index_value++)
      {
         const char *var_value = vscoreopt[index_key].values[index_value].value;
         uint8 value = 0;

         if (strcmp(var.value, var_value) != 0)
            continue;

         value = vsgame->core_options[index_key].settings[index_value].value;
         vsdip_new |= value;
      }
   }

   vsdip_new |= dipswitchPreset; /* Append any default preset to new value */
   if (last_vsdip != vsdip_new)
   {
      vsdip = vsdip_new;
#ifdef DEBUG
      FCEU_printf("Dipswitch changed %d%d%d%d%d%d%d%d\n",
         (vsdip >> 0) & 1, (vsdip >> 1) & 1, (vsdip >> 2) & 1, (vsdip >> 3) & 1,
         (vsdip >> 4) & 1, (vsdip >> 5) & 1, (vsdip >> 6) & 1, (vsdip >> 7) & 1);
#endif
   }
}

/* Nintendo World Championships 1990 */
static struct retro_core_option_v2_definition dipswitch_nwc[] = {
   {
      "fceumm_dipswitch_nwc",
      "Gameplay Duration in minutes (Restart)",
      NULL,
      "Sets the game timer in minutes.",
      NULL,
      "dip_switch",
      {
         { "0",  "5:00" },
         { "1",  "5:19" },
         { "2",  "5:38" },
         { "3",  "5:56" },
         { "4",  "6:15 (Tournament)" },
         { "5",  "6:34" },
         { "6",  "6:53" },
         { "7",  "7:11" },
         { "8",  "7:30" },
         { "9",  "7:49" },
         { "10", "8:08" },
         { "11", "8:27" },
         { "12", "8:45" },
         { "13", "9:04" },
         { "14", "9:23" },
         { "15", "9:42" },
         { NULL, NULL },
      },
      "4",
   },
   { NULL, NULL, NULL, NULL, NULL, NULL, {{0}}, NULL },
};

static void update_dipswitch_nwc(void)
{
   int dpsw_nwc = 0x00;
   struct retro_variable var = {
      "fceumm_dipswitch_nwc",
      NULL
   };

   if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
      dpsw_nwc = atoi(var.value);

   if (GameInfo->cspecial != dpsw_nwc)
   {
      GameInfo->cspecial = dpsw_nwc;
#ifdef DEBUG
      FCEU_printf("Dipswitch changed = %d%d%d%d\n", (dpsw_nwc >> 0) & 1,
         (dpsw_nwc >> 1) & 1, (dpsw_nwc >> 2) & 1, (dpsw_nwc >> 3) & 1);
#endif
   }
}

void set_dipswitch_variables(unsigned current_index, struct retro_core_option_v2_definition *vars)
{
   unsigned index = current_index;
   int dipsw_size = 0;

   /* VSUNI Dipswitch */
   if (GameInfo->type == GIT_VSUNI)
   {
      dipswitchPreset = FCEUI_VSUniGetDIPs();
      vsgame = get_vsuni_dipswitch(GameInfo->gameid);
      if (vsgame)
      {
         unsigned i, j;

         numCoreOptions = 0;
         dipswitch_type = DPSW_VSUNI;

         /* Initialize dipswitch struct with empty values */
         memset(&vscoreopt, 0, sizeof(vscoreopt));

         /* Count number of core options and the number of values for each */
         for (i = 0; i < MAX_CORE_OPTIONS; i++) {
            if (vsgame->core_options[i].option_name) {
               for (j = 0; j < MAX_VALUES; j++) {
                  if (vsgame->core_options[i].settings[j].name)
                     numValues[i]++;
               }
               numCoreOptions++;
            }
         }

         make_core_options(vscoreopt);

         /* Append dipswitch struct to main core options struct */
         while (vscoreopt[dipsw_size].key) {
            memcpy(&vars[index], &vscoreopt[dipsw_size],
                  sizeof(struct retro_core_option_v2_definition));
            index++;
            dipsw_size++;
         }
      }
      return;
   }

   /* Nintendo World Championship cart (Mapper 105)*/
   if (iNESCart.mapper == 105)
   {
      dipswitch_type = DPSW_NWC;

      while (dipswitch_nwc[dipsw_size].key) {
         memcpy(&vars[index], &dipswitch_nwc[dipsw_size],
               sizeof(struct retro_core_option_v2_definition));
         index++;
         dipsw_size++;
      }
      return;
   }

   dipswitch_type = DPSW_NONE;
   return;
}

void update_dipswitch(void)
{
   switch (dipswitch_type) {
   case DPSW_VSUNI:
      update_dipswitch_vsuni();
      break;
   case DPSW_NWC:
      update_dipswitch_nwc();
      break;
   }
}

void DPSW_Cleanup(void)
{
   unsigned i;

   for (i = 0; i < MAX_CORE_OPTIONS; i++) {
      if (core_key[i]) {
         free(core_key[i]);
         core_key[i] = NULL;
      }
   }
}
