/* Mednafen - Multi-system Emulator
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

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

#include "main.h"
#include "input.h"
#include "input-config.h"
#include "sound.h"
#include "video.h"
#include "joystick.h"
#include "netplay.h"
#include "cheat.h"
#include "fps.h"
#include "debugger.h"

static unsigned int autofirefreq;
static unsigned int ckdelay;
static bool fftoggle;

int ConfigDevice(int which, int arg, int, int system);
static void ConfigDeviceBegin(int ingame);
static void ConfigDeviceEnd(int ingame);

static void subcon_begin(void);
static int subcon(const char *text, ButtConfig *bc, int commandkey, int ingame);

static char keys[MKK_COUNT];
static uint32 MouseData[3];
static uint8 QuizKingData;
static uint8 HyperShotData;
static uint32 MahjongData;
static uint32 FTrainerData;
static uint8 TopRiderData;
static uint16 LynxPadData;
static uint8 NGPPadData;
static uint16 GBPadData;
static uint8 PCEPadData[5];
static uint8 NESPadData[4];
static uint8 BWorldData[1+13+1];

/* UsrInputType[] is user-specified.  InputType[] is current
        (game loading can override user settings)
*/
int UsrInputType[3]={SI_GAMEPAD,SI_GAMEPAD,SIFC_NONE};
int InputType[3];
static int cspec;
   
static int gametype;

/* Necessary for proper GUI functioning(configuring when a game isn't loaded). */
void InputUserActiveFix(void)
{
 int x;
 for(x=0;x<3;x++) InputType[x]=UsrInputType[x];
}

#define MK(x)		{{BUTTC_KEYBOARD},{0}, {MKK(x)},1}

#define MK_CK_SHIFT(x)	{{BUTTC_KEYBOARD}, {0}, {MKK(x) | (2<<24)}, 1}
#define MK_CK_ALT(x)	{{BUTTC_KEYBOARD}, {0}, {MKK(x) | (1<<24)}, 1}
#define MK_CK_CTRL(x)	{{BUTTC_KEYBOARD}, {0}, {MKK(x) | (4 << 24)}, 1}
#define MK_CK_CTRL_SHIFT(x) {{BUTTC_KEYBOARD}, {0}, {MKK(x) | (6 << 24)}, 1}

#define MK2(x1,x2)      {{BUTTC_KEYBOARD},{0},{MKK(x1),MKK(x2)},2}

#define MKZ()   {{0},{0},{0},0}

typedef enum {
	_CK_FIRST = 0,
        CK_SAVE_STATE = 0,
	CK_LOAD_STATE,
	CK_SAVE_MOVIE,
	CK_LOAD_MOVIE,
	CK_STATE_REWIND_TOGGLE,
	CK_0,CK_1,CK_2,CK_3,CK_4,CK_5,CK_6,CK_7,CK_8,CK_9,
	CK_M0,CK_M1,CK_M2,CK_M3,CK_M4,CK_M5,CK_M6,CK_M7,CK_M8,CK_M9,
	CK_TL1, CK_TL2, CK_TL3, CK_TL4, CK_TL5, CK_TL6, CK_TL7, CK_TL8, CK_TL9,
	CK_TAKE_SNAPSHOT,
	CK_TOGGLE_FS,
	CK_FAST_FORWARD,

	CK_INSERT_COIN,
	CK_TOGGLE_DIPVIEW,
	CK_SELECT_DISK,
	CK_INSERTEJECT_DISK,
	CK_ACTIVATE_BARCODE,

	CK_TOGGLE_FKB,

	CK_INPUT_CONFIG1,
	CK_INPUT_CONFIG2,
	CK_INPUT_CONFIGFC,
	CK_INPUT_CONFIGC,
        CK_INPUT_CONFIG3,
        CK_INPUT_CONFIG4,
	CK_INPUT_CONFIG5,
	CK_RESET,
	CK_POWER,
	CK_EXIT,
	CK_STATE_REWIND,
	CK_ROTATESCREEN,
	CK_TOGGLENETVIEW,
	CK_ADVANCE_FRAME,
	CK_RUN_NORMAL,
	CK_TOGGLECHEATVIEW,
	CK_TOGGLE_CHEAT_ACTIVE,
	CK_TOGGLE_FPS_VIEW,
	CK_TOGGLE_DEBUGGER,
	CK_STATE_SLOT_DEC,
        CK_STATE_SLOT_INC,
	_CK_COUNT
} CommandKey;

typedef struct __COKE
{
 ButtConfig bc;
 const char *text;
 unsigned int system;
 bool SkipCKDelay;
} COKE;

static COKE CKeys[_CK_COUNT]	=
{
	{ MK(F5), "save_state", ~0, 1 },
	{ MK(F7), "load_state", ~0, 0 },
	{ MK_CK_SHIFT(F5), "save_movie", ~0, 1 },
	{ MK_CK_SHIFT(F7), "load_movie", ~0, 0 },
	{ MK(F1), "state_rewind_toggle", ~0, 1 },
	{ MK(0), "0", ~0, 1},
        { MK(1), "1", ~0, 1},
        { MK(2), "2", ~0, 1},
        { MK(3), "3", ~0, 1},
        { MK(4), "4", ~0, 1},
        { MK(5), "5", ~0, 1},
        { MK(6), "6", ~0, 1},
        { MK(7), "7", ~0, 1},
        { MK(8), "8", ~0, 1},
        { MK(9), "9", ~0, 1},

	{ MK_CK_SHIFT(0), "m0", ~0, 1 },
        { MK_CK_SHIFT(1), "m1", ~0, 1 },
        { MK_CK_SHIFT(2), "m2", ~0, 1 },
        { MK_CK_SHIFT(3), "m3", ~0, 1 },
        { MK_CK_SHIFT(4), "m4", ~0, 1 },
        { MK_CK_SHIFT(5), "m5", ~0, 1 },
        { MK_CK_SHIFT(6), "m6", ~0, 1 },
        { MK_CK_SHIFT(7), "m7", ~0, 1 },
        { MK_CK_SHIFT(8), "m8", ~0, 1 },
        { MK_CK_SHIFT(9), "m9", ~0, 1 },

        { MK_CK_CTRL(1), "tl1", ~0, 1 },
        { MK_CK_CTRL(2), "tl2", ~0, 1 },
        { MK_CK_CTRL(3), "tl3", ~0, 1 },
        { MK_CK_CTRL(4), "tl4", ~0, 1 },
        { MK_CK_CTRL(5), "tl5", ~0, 1 },
        { MK_CK_CTRL(6), "tl6", ~0, 1 },
        { MK_CK_CTRL(7), "tl7", ~0, 1 },
        { MK_CK_CTRL(8), "tl8", ~0, 1 },
        { MK_CK_CTRL(9), "tl9", ~0, 1 },

	{ MK(F9), "take_snapshot", ~0, 1 },
	{ MK_CK_ALT(RETURN), "toggle_fs", ~0, 1 },
	{ MK(BACKQUOTE), "fast_forward", ~0, 1 },
	{ MK(F8), "insert_coin", ~0, 1 },
	{ MK(F6), "toggle_dipview", ~0, 1 },
	{ MK(F6), "select_disk", ~0, 1 },
	{ MK(F8), "insert_eject_disk", ~0, 1 },
	{ MK(F8), "activate_barcode", ~0, 1 },
	{ MK(SCROLLOCK), "toggle_fkb", ~0, 1 },
	{ MK(F3), "input_config1", ~0, 0 },
	{ MK_CK_SHIFT(F3), "input_config2", ~0, 0 },
	{ MK_CK_ALT(F3), "input_configfc", ~0, 0 },
        { MK(F2), "input_configc", ~0, 0 },
        { MK(F4), "input_config3", ~0, 0 },
        { MK_CK_SHIFT(F4), "input_config4", ~0, 0 },
	{ MK_CK_CTRL_SHIFT(F4), "input_config5", ~0, 0 },
	{ MK(F10), "reset", ~0, 0 },
	{ MK(F11), "power", ~0, 0 },
	{ MK2(F12, ESCAPE), "exit", ~0, 0 },
	{ MK(BACKSPACE), "state_rewind", ~0, 1 },
	{ MK(F8), "rotatescreen", ~0, 1 },

	{ MK_CK_ALT(n), "togglenetview", ~0, 1},
	{ MK_CK_ALT(a), "advance_frame", ~0, 1 },
	{ MK_CK_ALT(r), "run_normal", ~0, 1 },
	{ MK_CK_ALT(c), "togglecheatview", ~0, 1 },
	{ MK_CK_ALT(t), "togglecheatactive", ~0, 1 },
        { MK_CK_SHIFT(F1), "toggle_fps_view", ~0, 1 },
	{ MK_CK_ALT(d), "toggle_debugger", ~0, 1 },
	{ MK(MINUS), "state_slot_dec", ~0, 1 },
	{ MK(EQUALS), "state_slot_inc", ~0, 1 },
};

static int CKeysLastState[_CK_COUNT];
static uint32 CKeysPressTime[_CK_COUNT];
static uint32 CurTicks = 0;	// Optimization, SDL_GetTicks() might be slow on some platforms?

static int CK_Check(CommandKey which)
{
 int last = CKeysLastState[which];
 int tmp_ckdelay = ckdelay;

 if(CKeys[which].SkipCKDelay)
  tmp_ckdelay = 0;

 if((CKeysLastState[which] = DTestButtonCombo(&CKeys[which].bc, keys)))
 {
  if(!last)
   CKeysPressTime[which] = CurTicks;
 }
 else
  CKeysPressTime[which] = 0xFFFFFFFF;

 if(CurTicks >= ((int64)CKeysPressTime[which] + tmp_ckdelay))
 {
  CKeysPressTime[which] = 0xFFFFFFFF;
  return(1);
 }
 return(0);
}

static int CK_CheckActive(CommandKey which)
{
 return(DTestButtonCombo(&CKeys[which].bc, keys));
}

static void UpdateFKB(void);
static void UpdateGamepad(void);
static void UpdateQuizKing(void);
static void UpdateHyperShot(void);
static void UpdateMahjong(void);
static void UpdateFTrainer(void);
static void UpdateTopRider(void);

int NoWaiting = 0;

static int DIPS=0;

#define KEY(__a) keys[MKK(__a)]

static int cidisabled=0;
static int inff = 0;

typedef enum
{
	none,
	Port1,
	Port2,
	FCPort,
	Port3,
	Port4,
	Port5,
	Command
} ICType;

static ICType IConfig = none;
static int ICLatch;
static int RewindState = 0;

static bool NeedBLExitNow = 0;
bool MDFND_ExitBlockingLoop(void)
{
 SDL_Delay(1);
 memcpy(keys, SDL_GetKeyState(0), MKK_COUNT);

 CurTicks = SDL_GetTicks();

 if(CK_Check(CK_EXIT))
 {
       SDL_Event evt;
       NeedBLExitNow = 1;
       evt.quit.type = SDL_QUIT;
       SDL_PushEvent(&evt);
 }
 return(NeedBLExitNow);
}


static void KeyboardCommands(void)
{
  memcpy(keys, SDL_GetKeyState(0), MKK_COUNT);

  CurTicks = SDL_GetTicks();

  if(IConfig == none)
  {
   if(!IsConsoleCheatConfigActive())
    if(CK_Check(CK_TOGGLENETVIEW))
    {
     SDL_Event evt;
     evt.user.type = SDL_USEREVENT;
     evt.user.code = CEVT_NETPLAYTOGGLEVIEW;
     evt.user.data1 = 0;
     SDL_PushEvent(&evt);
    }

   if(!IsConsoleCheatConfigActive())
    if(CK_Check(CK_TOGGLE_DEBUGGER))
    {
     Debugger_Toggle();
    }

   if(!Debugger_IsActive() && !Netplay_GetTextView())
    if(CK_Check(CK_TOGGLECHEATVIEW))
    {
     ShowConsoleCheatConfig(!IsConsoleCheatConfigActive());
    }

   if(Netplay_GetTextView() || IsConsoleCheatConfigActive())
   {
    memset(keys, 0, sizeof(keys)); // This effectively disables keyboard input, but still
				   // allows physical joystick input when in the chat mode.
    //return;
   }

   if(Debugger_IsActive())
   {
    static char keys_backup[MKK_COUNT];
    memcpy(keys_backup, keys, MKK_COUNT);
    memset(keys, 0, sizeof(keys));

    keys[SDLK_F1] = keys_backup[SDLK_F1];
    keys[SDLK_F2] = keys_backup[SDLK_F2];
    keys[SDLK_F3] = keys_backup[SDLK_F3];
    keys[SDLK_F4] = keys_backup[SDLK_F4];
    keys[SDLK_F5] = keys_backup[SDLK_F5];
    keys[SDLK_F6] = keys_backup[SDLK_F6];
    keys[SDLK_F7] = keys_backup[SDLK_F7];
    keys[SDLK_F8] = keys_backup[SDLK_F8];
    keys[SDLK_F9] = keys_backup[SDLK_F9];
    keys[SDLK_F10] = keys_backup[SDLK_F10];
    keys[SDLK_F11] = keys_backup[SDLK_F11];
    keys[SDLK_F12] = keys_backup[SDLK_F12];
    keys[SDLK_F13] = keys_backup[SDLK_F13];
    keys[SDLK_F14] = keys_backup[SDLK_F14];
    keys[SDLK_F15] = keys_backup[SDLK_F15];
   }
  }

   if(IConfig == Port1)
   {
    if(CK_Check(CK_INPUT_CONFIG1))
    {
     ConfigDeviceEnd(1);
     IConfig = none;
     SetJoyReadMode(1);
     MDFNI_DispMessage((UTF8*)_("Configuration interrupted."));
    }
    else if(ConfigDevice(InputType[0], 0, 1, CurGame->system))
    {
     IConfig = none;
     SetJoyReadMode(1);
    }
   }
   else if(IConfig == Port2)
   {
    if(CK_Check(CK_INPUT_CONFIG1))
    {
     ConfigDeviceEnd(1);
     IConfig = none;
     SetJoyReadMode(1);
     MDFNI_DispMessage((UTF8*)_("Configuration interrupted."));
    }
    else if(ConfigDevice(InputType[1], 1, 1, CurGame->system))
    {
     IConfig = none;
     SetJoyReadMode(1);
    }
   }
   if(IConfig == Port3)
   {
    if(CK_Check(CK_INPUT_CONFIG3))
    {
     ConfigDeviceEnd(1);
     IConfig = none;
     SetJoyReadMode(1);
     MDFNI_DispMessage((UTF8*)_("Configuration interrupted."));
    }
    else if(ConfigDevice(InputType[0], 2, 1, CurGame->system))
    {
     IConfig = none;
     SetJoyReadMode(1);
    }
   }
   else if(IConfig == Port4)
   {
    if(CK_Check(CK_INPUT_CONFIG4))
    {
     ConfigDeviceEnd(1);
     IConfig = none;
     SetJoyReadMode(1);
     MDFNI_DispMessage((UTF8*)_("Configuration interrupted."));
    }
    else if(ConfigDevice(InputType[1], 3, 1, CurGame->system))
    {
     IConfig = none;
     SetJoyReadMode(1);
    }
   }
   else if(IConfig == Port5) // PC Engine only
   {
    if(CK_Check(CK_INPUT_CONFIG5))
    {
     ConfigDeviceEnd(1);
     IConfig = none;
     SetJoyReadMode(1);
     MDFNI_DispMessage((UTF8*)_("Configuration interrupted."));
    }
    else if(ConfigDevice(0, 4, 1, GISYS_PCE))
    {
     IConfig = none;
     SetJoyReadMode(1);
    }
   }
   else if(IConfig == FCPort)
   {
    if(CK_Check(CK_INPUT_CONFIG1))
    {
     ConfigDeviceEnd(1);
     IConfig = none;
     SetJoyReadMode(1);
     MDFNI_DispMessage((UTF8*)_("Configuration interrupted."));
    }
    if(ConfigDevice(InputType[2], 0, 1, CurGame->system))
    {
     IConfig = none;
     SetJoyReadMode(1);
    }
   }
   else if(IConfig == Command)
   {
    if(ICLatch != -1)
    {
     if(subcon(CKeys[ICLatch].text, &CKeys[ICLatch].bc, 1, 1))
     {
      MDFNI_DispMessage((UTF8*)_("Configuration finished."));
      IConfig = none;
      SetJoyReadMode(1);
      CKeysLastState[ICLatch] = 1;	// We don't want to accidentally
      return;				// trigger the command. :b
     }
    }
    else
    {
     int x;
     MDFNI_DispMessage((UTF8*)_("Press a command key now."));
     for(x = (int)_CK_FIRST; x < (int)_CK_COUNT; x++)
      if(CK_Check((CommandKey)x))
      {      
       ICLatch = x;
       subcon_begin();
       break;
      }
    }
   }

  if(CurGame)
  {
   if(InputType[2]==SIFC_FKB)
   {
    if(CK_Check(CK_TOGGLE_FKB))
    {
     cidisabled^=1;
     if(cidisabled)
      MDFNI_DispMessage((UTF8*)_("Family Keyboard enabled."));
     else
      MDFNI_DispMessage((UTF8*)_("Family Keyboard disabled."));
    }
    SDL_WM_GrabInput(cidisabled?SDL_GRAB_ON:SDL_GRAB_OFF);
    if(cidisabled) return;
   }
  }

  if(IConfig != none)
   return;

  if(CK_Check(CK_TOGGLE_CHEAT_ACTIVE))
  {
   bool isactive = MDFN_GetSettingB("cheats");
   
   isactive = !isactive;
   
   MDFNI_SetSetting("cheats", isactive);

   if(isactive)
    MDFNI_DispMessage((UTF8*)_("Application of cheats enabled."));
   else
    MDFNI_DispMessage((UTF8*)_("Application of cheats disabled."));
  }

  if(CK_Check(CK_EXIT))
  {
	SDL_Event evt;
	evt.quit.type = SDL_QUIT;
	SDL_PushEvent(&evt);
  }

  if(CK_Check(CK_TOGGLE_FPS_VIEW))
   FPS_ToggleView();

  if(CK_Check(CK_TOGGLE_FS)) 
  {
   SDL_Event evt;
   evt.user.type = SDL_USEREVENT;
   evt.user.code = CEVT_TOGGLEFS;
   SDL_PushEvent(&evt);
  }

  if(!CurGame)
	return;
  
  if(CK_Check(CK_ADVANCE_FRAME))
   DoFrameAdvance();

  if(CK_Check(CK_RUN_NORMAL))
   DoRunNormal();

  if(!Debugger_IsActive()) // We don't want to start button configuration when the debugger is active!
  {
   if(CK_Check(CK_INPUT_CONFIG1))
   {
    SetJoyReadMode(0);
    ConfigDeviceBegin(1);
    IConfig = Port1;
   }
   else if(CK_Check(CK_INPUT_CONFIG2))
   {
    SetJoyReadMode(0);
    ConfigDeviceBegin(1);
    IConfig = Port2;
   }
   else if(CK_Check(CK_INPUT_CONFIG3))
   {
    SetJoyReadMode(0);
    ConfigDeviceBegin(1);
    IConfig = Port3;
   }
   else if(CK_Check(CK_INPUT_CONFIG4))
   {
    SetJoyReadMode(0);
    ConfigDeviceBegin(1);
    IConfig = Port4;
   }
   else if(CK_Check(CK_INPUT_CONFIG5) && CurGame->system == GISYS_PCE)
   {
    SetJoyReadMode(0);
    ConfigDeviceBegin(1);
    IConfig = Port5;
   }
   else if(CK_Check(CK_INPUT_CONFIGFC))
   {
    SetJoyReadMode(0);
    ConfigDeviceBegin(1);
    IConfig = FCPort;
   }
   else if(CK_Check(CK_INPUT_CONFIGC))
   {
    SetJoyReadMode(0);
    ConfigDeviceBegin(1);
    ICLatch = -1;
    IConfig = Command;
   }
  }

 
 if(CurGame->system == GISYS_LYNX)
 {
  if(CK_Check(CK_ROTATESCREEN))
  {
   SDL_Event evt;

   CurGame->rotated++;
   if(CurGame->rotated > 1) CurGame->rotated = -1;
   evt.user.type = SDL_USEREVENT;
   evt.user.code = CEVT_VIDEOSYNC;
   SDL_PushEvent(&evt);
  }
 }
  if(CK_CheckActive(CK_STATE_REWIND))
	MDFNI_DoRewind();

  if(CK_Check(CK_STATE_REWIND_TOGGLE))
  {
   RewindState ^= 1;
   MDFNI_EnableStateRewind(RewindState);

   MDFNI_DispMessage(RewindState?(UTF8*)_("State rewinding functionality enabled."):(UTF8*)_("State rewinding functionality disabled."));
  }

  if(fftoggle)
  {
   if(CK_Check(CK_FAST_FORWARD))
   {
    inff = !inff;
    RefreshThrottleFPS(inff ? MDFN_GetSettingUI("ffspeed") : 1);
   }
  }
  else
  {
   if(CK_CheckActive(CK_FAST_FORWARD))
   {
    if(!inff) 
    {
     RefreshThrottleFPS(MDFN_GetSettingUI("ffspeed"));
     inff = 1;
    }
   }
   else
   {
    if(inff) 
    {
     RefreshThrottleFPS(1);
    }
    inff = 0;
   }
  }
  if(gametype==GIT_FDS)
  {
   if(CK_Check(CK_SELECT_DISK)) MDFNI_FDSSelect();
   if(CK_Check(CK_INSERTEJECT_DISK)) MDFNI_FDSInsert(0);
  }

  if(CK_Check(CK_TAKE_SNAPSHOT)) 
	pending_snapshot = 1;

  if(CK_Check(CK_SAVE_STATE))
	pending_save_state = 1;

  if(CK_Check(CK_SAVE_MOVIE))
	pending_save_movie = 1;

  if(CK_Check(CK_LOAD_STATE))
  {
	LockDebuggerMutex(1);
	MDFNI_LoadState(NULL, NULL);
	Debugger_ForceStepIfStepping();
	LockDebuggerMutex(0);
  }

  if(CK_Check(CK_LOAD_MOVIE))
  {
	LockDebuggerMutex(1);
	MDFNI_LoadMovie(NULL);
	Debugger_ForceStepIfStepping();
	LockDebuggerMutex(0);
  }

  if(CK_Check(CK_TL1))
   MDFNI_ToggleLayer(0);
  if(CK_Check(CK_TL2))
   MDFNI_ToggleLayer(1);
  if(CK_Check(CK_TL3))
   MDFNI_ToggleLayer(2);
  if(CK_Check(CK_TL4))
   MDFNI_ToggleLayer(3);
  if(CK_Check(CK_TL5))
   MDFNI_ToggleLayer(4);
  if(CK_Check(CK_TL6))
   MDFNI_ToggleLayer(5);
  if(CK_Check(CK_TL7))
   MDFNI_ToggleLayer(6);
  if(CK_Check(CK_TL8))
   MDFNI_ToggleLayer(7);
  if(CK_Check(CK_TL9))
   MDFNI_ToggleLayer(8);

  if(CK_Check(CK_RESET))
  {
	MDFNI_Reset();
	Debugger_ForceStepIfStepping();
  }

  if(CK_Check(CK_POWER))
  {
	MDFNI_Power();
	Debugger_ForceStepIfStepping();
  }

  if(CK_Check(CK_STATE_SLOT_INC))
  {
   MDFNI_SelectState(666 + 1);
  }

  if(CK_Check(CK_STATE_SLOT_DEC))
  {
   MDFNI_SelectState(666 - 1);
  }

  if(CurGame->system == GISYS_NES && gametype == GIT_VSUNI)
  {
	if(CK_Check(CK_INSERT_COIN))
		MDFNI_VSUniCoin();
	if(CK_Check(CK_TOGGLE_DIPVIEW))
        {
	 DIPS^=1;
	 MDFNI_VSUniToggleDIPView();
	}
	if(!(DIPS&1)) goto DIPSless;
	if(CK_Check(CK_1)) MDFNI_VSUniToggleDIP(0);
	if(CK_Check(CK_2)) MDFNI_VSUniToggleDIP(1);
	if(CK_Check(CK_3)) MDFNI_VSUniToggleDIP(2);
	if(CK_Check(CK_4)) MDFNI_VSUniToggleDIP(3);
	if(CK_Check(CK_5)) MDFNI_VSUniToggleDIP(4);
	if(CK_Check(CK_6)) MDFNI_VSUniToggleDIP(5);
	if(CK_Check(CK_7)) MDFNI_VSUniToggleDIP(6);
	if(CK_Check(CK_8)) MDFNI_VSUniToggleDIP(7);
  }
  else
  {
   static uint8 bbuf[32];
   static int bbuft;
   static int barcoder = 0;

   if(CurGame->system == GISYS_NES && ((InputType[2] == SIFC_BWORLD) || (cspec == SIS_DATACH)))
   {
    if(CK_Check(CK_ACTIVATE_BARCODE))
    {
     barcoder ^= 1;
     if(!barcoder)
     {
      if(InputType[2] == SIFC_BWORLD)
      {
       strcpy((char *)&BWorldData[1],(char *)bbuf);
       BWorldData[0]=1;
      }
      else
       MDFNI_DatachSet(bbuf);
      MDFNI_DispMessage((UTF8 *)_("Barcode Entered"));
     } 
     else { bbuft = 0; MDFNI_DispMessage((UTF8 *)_("Enter Barcode"));}
    }
   } else barcoder = 0;

   #define SSM(x) { if(bbuft < 13) {bbuf[bbuft++] = '0' + x; bbuf[bbuft] = 0;} MDFNI_DispMessage((UTF8 *)_("Barcode: %s"),bbuf); }
   DIPSless:

   if(barcoder)
   {
    if(CK_Check(CK_0)) SSM(0);
    if(CK_Check(CK_1)) SSM(1);
    if(CK_Check(CK_2)) SSM(2);
    if(CK_Check(CK_3)) SSM(3);
    if(CK_Check(CK_4)) SSM(4);
    if(CK_Check(CK_5)) SSM(5);
    if(CK_Check(CK_6)) SSM(6);
    if(CK_Check(CK_7)) SSM(7);
    if(CK_Check(CK_8)) SSM(8);
    if(CK_Check(CK_9)) SSM(9);
   }
   else
   {
    if(CK_Check(CK_0)) MDFNI_SelectState(0);
    if(CK_Check(CK_1)) MDFNI_SelectState(1);
    if(CK_Check(CK_2)) MDFNI_SelectState(2);
    if(CK_Check(CK_3)) MDFNI_SelectState(3);
    if(CK_Check(CK_4)) MDFNI_SelectState(4);
    if(CK_Check(CK_5)) MDFNI_SelectState(5);
    if(CK_Check(CK_6)) MDFNI_SelectState(6);
    if(CK_Check(CK_7)) MDFNI_SelectState(7);
    if(CK_Check(CK_8)) MDFNI_SelectState(8);
    if(CK_Check(CK_9)) MDFNI_SelectState(9);

    if(CK_Check(CK_M0)) MDFNI_SelectMovie(0);
    if(CK_Check(CK_M1)) MDFNI_SelectMovie(1);
    if(CK_Check(CK_M2)) MDFNI_SelectMovie(2);
    if(CK_Check(CK_M3)) MDFNI_SelectMovie(3);
    if(CK_Check(CK_M4)) MDFNI_SelectMovie(4);
    if(CK_Check(CK_M5)) MDFNI_SelectMovie(5);
    if(CK_Check(CK_M6)) MDFNI_SelectMovie(6);
    if(CK_Check(CK_M7)) MDFNI_SelectMovie(7);
    if(CK_Check(CK_M8)) MDFNI_SelectMovie(8);
    if(CK_Check(CK_M9)) MDFNI_SelectMovie(9);
   }
   #undef SSM
 }
}

#define GPZ()   {MKZ(), MKZ(), MKZ(), MKZ()}

ButtConfig GamePadConfig[4][10]={
        /* Gamepad 1 */
        { 
	 MK(KP3), MK(KP2), MK(TAB), MK(RETURN), MK(w),MK(z),
                MK(a), MK(s), MKZ(), MKZ()
	},

        /* Gamepad 2 */
        GPZ(),

        /* Gamepad 3 */
        GPZ(),
  
        /* Gamepad 4 */
        GPZ()
};


static void UpdateGamepad(void)
{
 static unsigned int rapid=0;
 unsigned int x;
 unsigned int wg;

 rapid = (rapid + 1) % (autofirefreq + 1);
 memset(NESPadData, 0, sizeof(NESPadData));

 for(wg=0;wg<4;wg++)
 {
  for(x=0;x<8;x++)
   if(DTestButton(&GamePadConfig[wg][x], keys, MouseData))
    NESPadData[wg] |= (1<<x);

  if(rapid >= (autofirefreq + 1) / 2)
   for(x=0;x<2;x++)
     if(DTestButton(&GamePadConfig[wg][8+x], keys, MouseData))
      NESPadData[wg] |= (1 << x);
 }

 for(wg=0;wg<4;wg++)	/* Now, test to see if anything weird(up+down at same time)
			   is happening, and correct */
 {
  if((NESPadData[wg] & 0xC0) == 0xC0) NESPadData[wg] &= ~0xC0;
  if((NESPadData[wg] & 0x30) == 0x30) NESPadData[wg] &= ~0x30;
 }
}

ButtConfig GBPadConfig[12] = 
{
         MK(KP3), MK(KP2), MK(TAB), MK(RETURN), MK(s),MK(a),
                MK(w), MK(z), MK(KP6), MK(KP5), MK(KP9), MK(KP8)
};

static void UpdateGBGamepad(void)
{
 static unsigned int rapid = 0;
 int x;

 rapid = (rapid + 1) % (autofirefreq + 1);

 GBPadData = 0;

 for(x=0;x<10;x++)
  if(DTestButton(&GBPadConfig[x], keys, MouseData))
   GBPadData |= 1 << x;

 if(rapid >= (autofirefreq + 1) / 2)
  for(x = 0; x < 2; x++)
   if(DTestButton(&GBPadConfig[10 + x], keys, MouseData))
    GBPadData |= 1 << x;
}

ButtConfig PCEPadConfig[5][10]={
        /* Gamepad 1 */
        {
         MK(KP3), MK(KP2), MK(TAB), MK(RETURN), MK(w),MK(s),
                MK(z), MK(a), MK(KP6), MK(KP5)
        },

        /* Gamepad 2 */
        GPZ(),

        /* Gamepad 3 */
        GPZ(),

        /* Gamepad 4 */
        GPZ(),
	
	/* Gamepad 5 */
	GPZ()
};

static void UpdatePCEGamepad(void)
{
 static unsigned int rapid = 0;

 rapid = (rapid + 1) % (autofirefreq + 1);

 for(int i = 0; i < 5; i++)
 {
  PCEPadData[i] = 0;
  for(int x = 0; x < 8; x++)
  {
   if(DTestButton(&PCEPadConfig[i][x], keys, MouseData))
    PCEPadData[i] |= 1 << x;
  }
  if(rapid >= (autofirefreq + 1) / 2)
   for(int x = 0; x < 2; x++)
    if(DTestButton(&PCEPadConfig[i][8 + x], keys, MouseData))
     PCEPadData[i] |= 1 << x;

  if((PCEPadData[i] & 0xA0) == 0xA0) // left + right at same time!
   PCEPadData[i] ^= 0xA0;
  if((PCEPadData[i] & 0x50) == 0x50) // up + down at same time
   PCEPadData[i] ^= 0x50;
 }
}


ButtConfig LynxPadConfig[9] =
{
	// A, B, Option 2, Option 1, Left, Right, Up, Down, Pause
         MK(KP3), MK(KP2), MK(KP1), MK(KP7), MK(a),MK(s),
                MK(w), MK(z), MK2(KP4, RETURN)
};


static void UpdateLynxGamepad(void)
{
 int x;

 LynxPadData = 0;

 for(x=0;x<9;x++)
  if(DTestButton(&LynxPadConfig[x], keys, MouseData))
   LynxPadData |= 1 << x;;

 if(CurGame->rotated == -1)
 {
  int d = LynxPadData;

  LynxPadData = (d & 0x10F) | ((d & 0x10) << 3) | ((d & 0x20) << 1) | ((d & 0x40) >> 2) | ((d & 0x80) >> 2);
 }
 else if(CurGame->rotated == 1)
 {
  int d = LynxPadData;
  LynxPadData = (d & 0x10F) | ((d & 0x10) << 2) | ((d & 0x20) << 2) | ((d & 0x80) >> 3) | ((d & 0x40) >> 1);
 }
}


ButtConfig NGPPadConfig[7 + 2] =
{
	// Up, down, left, right, a(inner), b(outer), option
	MK(w), MK(z), MK(a), MK(s), MK(KP2), MK(KP3), MK(RETURN), MK(KP5), MK(KP6)
};


static void UpdateNGPGamepad(void)
{
 int x;
 static unsigned int rapid = 0;

 rapid = (rapid + 1) % (autofirefreq + 1);

 NGPPadData = 0;

 for(x=0;x<7;x++)
  if(DTestButton(&NGPPadConfig[x], keys, MouseData))
   NGPPadData |= 1 << x;;

 if(rapid >= (autofirefreq + 1) / 2)
  for(int x = 0; x < 2; x++)
   if(DTestButton(&NGPPadConfig[7 + x], keys, MouseData))
    NGPPadData |= 1 << (x + 4);

 if((NGPPadData & 0x03) == 0x03) // up + down at same time
  NGPPadData ^= 0x03;
 if((NGPPadData & 0x0C) == 0x0C) // left + right at same time
  NGPPadData ^= 0x0C;

}

ButtConfig powerpadsc[2][12]={
                              {
                            	MK(o),MK(p),MK(LEFTBRACKET),MK(RIGHTBRACKET),
				MK(k),MK(l),MK(SEMICOLON),MK(QUOTE),
                              	MK(m),MK(COMMA),MK(PERIOD),MK(SLASH)
                              },
                              {
                        	MK(o),MK(p),MK(LEFTBRACKET),MK(RIGHTBRACKET),
				MK(k),MK(l),MK(SEMICOLON),MK(QUOTE),
                               	MK(m),MK(COMMA),MK(PERIOD),MK(SLASH)
                              }
                             };

static uint32 powerpadbuf[2];

static uint32 UpdatePPadData(int w)
{
 uint32 r=0;
 ButtConfig *ppadtsc=powerpadsc[w];
 int x;

 for(x=0;x<12;x++)
  if(DTestButton(&ppadtsc[x], keys, MouseData)) r|=1<<x;

 return r;
}

static uint8 fkbkeys[0x48];

void MDFND_UpdateInput(void)
{
  int x;
  int t=0;

  {
   int x,y;
   MouseData[2] = SDL_GetMouseState(&x,&y);
   PtoV(&x,&y);
   MouseData[0]=x&0xFFFF;
   MouseData[1]=y&0xFFFF;
  }

  SDL_JoystickUpdate();
  KeyboardCommands();

  if(CurGame)
  {
   if(CurGame->system == GISYS_GB || CurGame->system == GISYS_GBA)
   {
    UpdateGBGamepad();
   }
   else if(CurGame->system == GISYS_LYNX)
    UpdateLynxGamepad();
   else if(CurGame->system == GISYS_NGP)
    UpdateNGPGamepad();
   else if(CurGame->system == GISYS_PCE)
   {
    UpdatePCEGamepad();
   }
   else
   {
    for(x=0;x<2;x++)
     switch(InputType[x])
     {
      case SI_GAMEPAD:t|=1;break;
      case SI_ARKANOID:t|=2;break;
      case SI_ZAPPER:t|=2;break;
      case SI_POWERPADA:
      case SI_POWERPADB:powerpadbuf[x]=UpdatePPadData(x);break;
     }

    switch(InputType[2])
    {
     case SIFC_ARKANOID:t|=2;break;
     case SIFC_SHADOW:t|=2;break;
     case SIFC_FKB:if(cidisabled) UpdateFKB();break;
     case SIFC_HYPERSHOT: UpdateHyperShot();break;
     case SIFC_MAHJONG: UpdateMahjong();break;
     case SIFC_QUIZKING: UpdateQuizKing();break;
     case SIFC_FTRAINERB:
     case SIFC_FTRAINERA: UpdateFTrainer();break;
     case SIFC_TOPRIDER: UpdateTopRider();break;
     case SIFC_OEKAKIDS:t|=2;break;
    }

    if(t&1)
     UpdateGamepad();
   }
 }
}

static void FixBCs(void);

void MDFNI_SetGBInput(uint8 *);

static const char *fccortab[11]={"none","arkanoid","shadow","4player","fkb","hypershot",
                        "mahjong","quizking","ftrainera","ftrainerb","oekakids"};
static const int fccortabi[11]={SIFC_NONE,SIFC_ARKANOID,SIFC_SHADOW,
                                 SIFC_4PLAYER,SIFC_FKB,SIFC_HYPERSHOT,SIFC_MAHJONG,SIFC_QUIZKING,
                                 SIFC_FTRAINERA,SIFC_FTRAINERB,SIFC_OEKAKIDS};

static char *cortab[6]={"none","gamepad","zapper","powerpada","powerpadb","arkanoid"};
static int cortabi[6]={SI_NONE,SI_GAMEPAD,SI_ZAPPER,SI_POWERPADA,SI_POWERPADB,SI_ARKANOID};

void InitOtherInput(MDFNGI *gi)
{
 autofirefreq = MDFN_GetSettingUI("autofirefreq");
 fftoggle = MDFN_GetSettingB("fftoggle");
 ckdelay = MDFN_GetSettingUI("ckdelay");

 memset(CKeysPressTime, 0xff, sizeof(CKeysPressTime));
 memset(CKeysLastState, 0, sizeof(CKeysLastState));

 if(CurGame->system == GISYS_GB || CurGame->system == GISYS_GBA)
 {
  MDFNI_SetInput(0,0,&GBPadData,0);
 }
 else if(CurGame->system == GISYS_LYNX)
 {
  MDFNI_SetInput(0, 0, &LynxPadData, 0);
 }
 else if(CurGame->system == GISYS_NGP)
 {
  MDFNI_SetInput(0, 0, &NGPPadData, 0);
 }
 else if(CurGame->system == GISYS_PCE)
 {
  MDFNI_SetInput(0, 0, PCEPadData, 0);
 }
 else
 {
  void *InputDPtr;
  int t,x,attrib;
  std::string ip1_str = MDFN_GetSettingS("nes.input1");
  std::string ip2_str = MDFN_GetSettingS("nes.input2");
  std::string fcexp_str = MDFN_GetSettingS("nes.fcexp");

  for(int y = 0; y < 11; y++)
   if(!strcasecmp(fccortab[y], fcexp_str.c_str()))
    UsrInputType[2] = fccortabi[y];

  for(int y = 0; y < 6; y++)
   if(!strcasecmp(cortab[y], ip1_str.c_str()))
    UsrInputType[0] = cortabi[y];

  for(int y = 0; y < 6; y++)
   if(!strcasecmp(cortab[y], ip2_str.c_str()))
    UsrInputType[1] = cortabi[y];

  gametype=gi->nes.type;

  InputType[0] = UsrInputType[0];
  InputType[1] = UsrInputType[1];
  InputType[2] = UsrInputType[2];

  if(gi->nes.input[0]>=0)
   InputType[0]=gi->nes.input[0];
  if(gi->nes.input[1]>=0)
   InputType[1]=gi->nes.input[1];
  if(gi->nes.inputfc>=0)
   InputType[2]=gi->nes.inputfc;
  cspec = gi->nes.cspecial;

   for(t=0,x=0;x<2;x++)
   {
    attrib=0;
    InputDPtr = NULL;
    switch(InputType[x])
    {
     case SI_POWERPADA:
     case SI_POWERPADB:InputDPtr=&powerpadbuf[x];break;
     case SI_GAMEPAD:InputDPtr = NESPadData;break;     
     case SI_ARKANOID:InputDPtr=MouseData;t|=1;break;
     case SI_ZAPPER:InputDPtr=MouseData;
                                t|=1;
                                attrib=1;
                                break;
    }
    MDFNI_SetInput(x, InputType[x], InputDPtr, attrib);
   }

   attrib=0;
   InputDPtr=0;
   switch(InputType[2])
   {
    case SIFC_SHADOW:InputDPtr=MouseData;t|=1;attrib=1;break;
    case SIFC_OEKAKIDS:InputDPtr=MouseData;t|=1;attrib=1;break;
    case SIFC_ARKANOID:InputDPtr=MouseData;t|=1;break;
    case SIFC_FKB:InputDPtr=fkbkeys;break;
    case SIFC_HYPERSHOT:InputDPtr=&HyperShotData;break;
    case SIFC_MAHJONG:InputDPtr=&MahjongData;break;
    case SIFC_QUIZKING:InputDPtr=&QuizKingData;break;
    case SIFC_TOPRIDER:InputDPtr=&TopRiderData;break;
    case SIFC_BWORLD:InputDPtr=BWorldData;break;
    case SIFC_FTRAINERA:
    case SIFC_FTRAINERB:InputDPtr=&FTrainerData;break;
   }

   MDFNI_SetInputFC(InputType[2],InputDPtr,attrib);
 }

 FixBCs();

 SetJoyReadMode(1); // Disable joystick event handling, and allow manual state updates.
}


ButtConfig fkbmap[0x48]=
{
 MK(F1),MK(F2),MK(F3),MK(F4),MK(F5),MK(F6),MK(F7),MK(F8),
 MK(1),MK(2),MK(3),MK(4),MK(5),MK(6),MK(7),MK(8),MK(9),MK(0),MK(MINUS),MK(EQUALS),MK(BACKSLASH),MK(BACKSPACE),
 MK(ESCAPE),MK(q),MK(w),MK(e),MK(r),MK(t),MK(y),MK(u),MK(i),MK(o),MK(p),MK(BACKQUOTE),MK(LEFTBRACKET),MK(RETURN),
 MK(LCTRL),MK(a),MK(s),MK(d),MK(f),MK(g),MK(h),MK(j),MK(k),MK(l),MK(SEMICOLON),MK(QUOTE),MK(RIGHTBRACKET),MK(INSERT),
 MK(LSHIFT),MK(z),MK(x),MK(c),MK(v),MK(b),MK(n),MK(m),MK(COMMA),MK(PERIOD),MK(SLASH),MK(RALT),MK(RSHIFT),MK(LALT),MK(SPACE),
 MK(DELETE),MK(END),MK(PAGEDOWN),MK(UP),MK(LEFT),MK(RIGHT),MK(DOWN)
};

static void UpdateFKB(void)
{
 int x;

 for(x=0;x<0x48;x++)
 {
  fkbkeys[x]=0;

  if(DTestButton(&fkbmap[x], keys, MouseData))
   fkbkeys[x]=1;
 }
}

static ButtConfig HyperShotButtons[4]=
{
 MK(q),MK(w),MK(e),MK(r)
};

static void UpdateHyperShot(void)
{
 int x;

 HyperShotData=0;
 for(x=0;x<0x4;x++)
 {
  if(DTestButton(&HyperShotButtons[x], keys, MouseData))
   HyperShotData|=1<<x;
 }
}

static ButtConfig MahjongButtons[21]=
{
 MK(q),MK(w),MK(e),MK(r),MK(t),
 MK(a),MK(s),MK(d),MK(f),MK(g),MK(h),MK(j),MK(k),MK(l),
 MK(z),MK(x),MK(c),MK(v),MK(b),MK(n),MK(m)
};

static void UpdateMahjong(void)
{
 int x;
        
 MahjongData=0;
 for(x=0;x<21;x++)
 {  
  if(DTestButton(&MahjongButtons[x], keys, MouseData))
   MahjongData|=1<<x;
 }
}

ButtConfig QuizKingButtons[6]=
{
 MK(q),MK(w),MK(e),MK(r),MK(t),MK(y)
};

static void UpdateQuizKing(void)
{
 int x;

 QuizKingData=0;

 for(x=0;x<6;x++)
 {
  if(DTestButton(&QuizKingButtons[x], keys, MouseData))
   QuizKingData|=1<<x;
 }

}

ButtConfig TopRiderButtons[8]=
{
 MK(q),MK(w),MK(e),MK(r),MK(t),MK(y),MK(u),MK(i)
};

static void UpdateTopRider(void)
{
 int x;
 TopRiderData=0;
 for(x=0;x<8;x++)
  if(DTestButton(&TopRiderButtons[x], keys, MouseData))
   TopRiderData|=1<<x;
}

ButtConfig FTrainerButtons[12]=
{
                               MK(o),MK(p),MK(LEFTBRACKET),
                               MK(RIGHTBRACKET),MK(k),MK(l),MK(SEMICOLON),
                                MK(QUOTE),
                               MK(m),MK(COMMA),MK(PERIOD),MK(SLASH)
};

static void UpdateFTrainer(void)
{
 int x;

 FTrainerData=0;

 for(x=0;x<12;x++)
 {
  if(DTestButton(&FTrainerButtons[x], keys, MouseData))
   FTrainerData|=1<<x;
 }
}

static int subcon_tb;
static int subcon_wc;
static int jitter_correct;

static void subcon_begin(void)
{
 subcon_tb = -1;
 subcon_wc = 0;
 jitter_correct = 0;
}

/* Configures an individual virtual button. */
static int subcon(const char *text, ButtConfig *bc, int commandkey, int ingame)
{
 char buf[256];

 while(subcon_wc < MAXBUTTCONFIG)
 {
  sprintf(buf,"%s (%d)",text,subcon_wc+1);
  //puts(buf);
  if(ingame)
  {
   MDFNI_DispMessage((UTF8*)buf);
   if(subcon_tb != subcon_wc)
   {
    DTryButtonBegin(bc, subcon_wc, commandkey);
    subcon_tb = subcon_wc;
    jitter_correct = 0;
   }
   if(!jitter_correct)
    if(!DTryButton())
     return(0);
  }
  else
   DWaitButton(buf,bc,subcon_wc, commandkey);

  if(ingame)
    DTryButtonEnd(bc);

  if(bc->ButtType[subcon_wc] != BUTTC_JOYSTICK)	// Only do jitter correction on joysticks.
   jitter_correct = 10;

  redojitter:	// for (!ingame) condition.

  if(jitter_correct < 10)
  {
   ButtConfig tmpbc;

   tmpbc.ButtType[0] = bc->ButtType[subcon_wc];
   tmpbc.DeviceNum[0] = bc->DeviceNum[subcon_wc];
   tmpbc.ButtonNum[0] = bc->ButtonNum[subcon_wc];
   tmpbc.DeviceID[0] = bc->DeviceID[subcon_wc];
   tmpbc.NumC = 1;

   if(!ingame)
    memcpy(keys, SDL_GetKeyState(0), MKK_COUNT);
   if((!commandkey && DTestButton(&tmpbc, keys, MouseData)) || (commandkey && DTestButtonCombo(&tmpbc, keys)))
   {
    jitter_correct++;
    if(!ingame) goto redojitter;
   }
   else
   {
    //puts("Jitter failure!");
    subcon_tb = subcon_wc - 1;	// Redo this physical button.
   }
   if(ingame)
    return(0);
   else
    continue;
  }

  if(subcon_wc && bc->ButtType[subcon_wc]==bc->ButtType[subcon_wc-1] && bc->DeviceNum[subcon_wc]==bc->DeviceNum[subcon_wc-1] &&
               bc->ButtonNum[subcon_wc]==bc->ButtonNum[subcon_wc-1])
        break;
  subcon_wc++;  
 }
 bc->NumC = subcon_wc;
 //puts("DONE");
 return(1);
}

void ConfigCommandKey(char *which)
{
 int which_n;
 for(which_n = (int)_CK_FIRST; which_n < (int)_CK_COUNT; which_n++)
 {
  if(!strcmp(CKeys[which_n].text, which))
  {
   subcon_begin();
   subcon(CKeys[which_n].text, &CKeys[which_n].bc, 1, 0);
  }
 }
}

int MDFND_CKConfig(const char *name, const char *text)
{
 unsigned int slen = strlen(text);
 char *tmp = (char *)malloc(slen + 1);
 char* lastptr = tmp;
 unsigned int x;

 ButtonConfigBegin();
 strcpy(tmp, text);
 for(x=0;x<slen;x++)
 {
  if(tmp[x] == ',' || tmp[x] == ' ')
  {
   tmp[x] = 0;
   ConfigCommandKey(lastptr);
   lastptr = &tmp[x+1];
  }
 }
 ConfigCommandKey(lastptr);
 ButtonConfigEnd();
 free(tmp);

 return(1);
}

static int cd_x;
static int cd_lx = -1;
static void ConfigDeviceBegin(int ingame)
{
 cd_x = 0;
 cd_lx = -1;

 if(!ingame)
  ButtonConfigBegin();
}

static void ConfigDeviceEnd(int ingame)
{
 if(!ingame)
  ButtonConfigEnd();
}

int ConfigDevice(int which, int arg, int ingame, int system)
{
 char buf[256];

 if(!ingame)
  ConfigDeviceBegin(0);

 if(system == GISYS_LYNX)
 {
  char *str[9] = { _("A (outer)"), _("B (inner)"), _("Option 2 (lower)"), _("Option 1 (upper)"), _("LEFT ←"), _("RIGHT →"), _("UP ↑"), _("DOWN ↓"), "PAUSE" };
  for(;cd_x<9;cd_x++)
  {
   sprintf(buf, "Pad: %s",str[cd_x]);
   if(cd_x != cd_lx)
   {
    cd_lx = cd_x;
    subcon_begin();
   }
   if(!subcon(buf,&LynxPadConfig[cd_x], 0, ingame))
    return(0);
  }
 }
 else if(system == GISYS_NGP)
 {
  char *str[9] = { _("UP ↑"), _("DOWN ↓"), _("LEFT ←"), _("RIGHT →"), "A", "B", "OPTION", _("RAPID A"), _("RAPID B") };
  for(;cd_x<9;cd_x++)
  {
   sprintf(buf, "Pad: %s",str[cd_x]);
   if(cd_x != cd_lx)
   {
    cd_lx = cd_x;
    subcon_begin();
   }
   if(!subcon(buf,&NGPPadConfig[cd_x], 0, ingame))
    return(0);
  }
 }
 else if(system == GISYS_GB || system == GISYS_GBA)
 {
  char *str[12] =  { "A", "B", "SELECT", "START", _("RIGHT →"), _("LEFT ←"), _("UP ↑"), _("DOWN ↓"), _("SHOULDER R"), _("SHOULDER L"), _("RAPID A"), _("RAPID B") };
  for(;cd_x<12;cd_x++)
  {
   sprintf(buf, "Pad: %s",str[cd_x]);
   if(cd_x != cd_lx)
   {
    cd_lx = cd_x;
    subcon_begin();
   }
   if(!subcon(buf,&GBPadConfig[cd_x], 0, ingame))
    return(0);
  }
 }
 else if(system == GISYS_PCE)
 {
  char *str[10] =  { "I", "II", "SELECT", "RUN", _("UP ↑"), _("RIGHT →"), _("DOWN ↓"), _("LEFT ←"), _("RAPID I"), _("RAPID II") };
  for(;cd_x<10;cd_x++)
  {
   sprintf(buf,"GamePad #%d: %s",arg+1,str[cd_x]);
   if(cd_x != cd_lx)
   {
    cd_lx = cd_x;
    subcon_begin();
   }
   if(!subcon(buf,&PCEPadConfig[arg][cd_x], 0, ingame))
    return(0);
  }
 }
 else
 switch(which)
 {
  case SIFC_QUIZKING:
   for(;cd_x<6;cd_x++)
   {
    sprintf(buf,"Quiz King Buzzer #%d", cd_x+1);
    if(cd_x != cd_lx)
    {
     cd_lx = cd_x;
     subcon_begin();
    }
    if(!subcon(buf,&QuizKingButtons[cd_x], 0, ingame))
     return(0);
   }
   break;
  case SIFC_HYPERSHOT:
   for(;cd_x<4;cd_x++)
   {
    sprintf(buf,"Hyper Shot %d: %s",((cd_x&2)>>1)+1,(cd_x&1)?"JUMP":"RUN");
    if(cd_x != cd_lx)
    {
     cd_lx = cd_x;
     subcon_begin();
    }
    if(!subcon(buf,&HyperShotButtons[cd_x], 0, ingame))
     return(0);
   } 
   break;
  case SI_POWERPADA:
  case SI_POWERPADB:
   for(;cd_x<12;cd_x++)
   {
    sprintf(buf,"PowerPad %d: %d", (arg&1)+1,cd_x+1);
    if(cd_x != cd_lx)
    {
     cd_lx = cd_x;
     subcon_begin();
    }
    if(!subcon(buf,&powerpadsc[arg&1][cd_x], 0, ingame))
     return(0);
   }
   break;

  case SIFC_FTRAINERA:
  case SIFC_FTRAINERB:
   for(;cd_x<12;cd_x++)
   {
    sprintf(buf,"Family Trainer: %d", cd_x+1);
    if(cd_x != cd_lx)
    {
     cd_lx = cd_x;
     subcon_begin();
    }
    if(!subcon(buf,&FTrainerButtons[cd_x], 0, ingame))
     return(0);
   }
   break;

  case SI_GAMEPAD:
  {
   char *str[10]={"A","B","SELECT","START","UP ↑","DOWN ↓","LEFT ←","RIGHT →","Rapid A","Rapid B"};
   for(;cd_x<10;cd_x++)
   {
    sprintf(buf,"GamePad #%d: %s",arg+1,str[cd_x]);
    if(cd_x != cd_lx)
    {
     cd_lx = cd_x;
     subcon_begin();
    }
    if(!subcon(buf,&GamePadConfig[arg][cd_x], 0, ingame))
     return(0);
   }
  }
  break;
 }

 if(!ingame)
  ConfigDeviceEnd(0);
 else
 {
  MDFNI_DispMessage((UTF8*)_("Configuration finished."));
 }
 return(1);
}


static CFGSTRUCT InputCKConfig[_CK_COUNT + 1];

void MakeInputConfigStruct(void)
{
 int x;

 for(x = 0; x < _CK_COUNT; x++)
 {
  InputCKConfig[x].name = CKeys[x].text;
  InputCKConfig[x].ptr = &CKeys[x].bc;
  InputCKConfig[x].len = sizeof(ButtConfig);
 }
 InputCKConfig[x].name = 0;
 InputCKConfig[x].ptr = 0;
 InputCKConfig[x].len = 0;
}

CFGSTRUCT InputConfig[] =
{
        AC(powerpadsc),
        AC(QuizKingButtons),
        AC(FTrainerButtons),
        AC(HyperShotButtons),
        AC(MahjongButtons),
        AC(GamePadConfig),

//	AC(GBPadConfig),
	{ "GBPadConfig", GBPadConfig, sizeof(ButtConfig) * 10 },	// Compatibility for pre 0.4.9
	NAC("NewGBPadConfig", GBPadConfig),

	AC(LynxPadConfig),
	AC(NGPPadConfig),
	AC(PCEPadConfig),
        AC(fkbmap),
	ADDCFGSTRUCT(InputCKConfig),
        ENDCFGSTRUCT
};


int MDFND_InputCfg(const char *name, const char *text)
{
         if(!strncasecmp(text,"gamepad",strlen("gamepad")))
         {
          ConfigDevice(SI_GAMEPAD,(text[strlen("gamepad")]-'1')&3, 0, GISYS_NES);
         }
         else if(!strncasecmp(text,"pcegamepad",strlen("pcegamepad")))
         {
          ConfigDevice(SI_GAMEPAD,(text[strlen("pcegamepad")]-'1') % 5, 0, GISYS_PCE);
         }
         else if(!strncasecmp(text,"powerpad",strlen("powerpad")))
         {
          ConfigDevice(SI_POWERPADA,(text[strlen("powerpad")]-'1')&1, 0, GISYS_NES);
         }
         else if(!strcasecmp(text,"hypershot"))
	 {
	  ConfigDevice(SIFC_HYPERSHOT,0, 0, GISYS_NES);
	 }
	 else if(!strcasecmp(text, "ftrainer"))
	 {
	  ConfigDevice(SIFC_FTRAINERB, 0, 0, GISYS_NES);
	 }
         else if(!strcasecmp(text,"quizking"))
          ConfigDevice(SIFC_QUIZKING,0, 0, GISYS_NES);

	return(1);
}

bool MDFND_ValidateNESInputPort(const char *name, const char *value)
{
 for(int y = 0; y < 6; y++)
  if(!strcasecmp(cortab[y], value))
   return(1);

 return(0);
}

bool MDFND_ValidateFamicomExpansionPort(const char *name, const char *value)
{
 for(int y = 0; y < 11; y++)
  if(!strcasecmp(fccortab[y], value))
   return(1);

 return(0);
}

static void FixBCs(void)
{
 unsigned int x, y;


 for(x=0; x<_CK_COUNT;x++)
  JoyClearBC(&CKeys[x].bc);

   for(x=0; x<4; x++)
    for(y=0; y<10; y++)
     JoyClearBC(&GamePadConfig[x][y]);

   for(x=0; x<2; x++)
    for(y=0; y<12; y++)
     JoyClearBC(&powerpadsc[x][y]);

   for(x=0; x<0x48; x++)
    JoyClearBC(&fkbmap[x]);

   for(x=0; x<6; x++)
    JoyClearBC(&QuizKingButtons[x]);
   for(x=0; x<12; x++)
    JoyClearBC(&FTrainerButtons[x]);
   for(x=0; x<21; x++)
    JoyClearBC(&MahjongButtons[x]);
   for(x=0; x<4; x++)
    JoyClearBC(&HyperShotButtons[x]);

 for(x=0;x<12;x++)
  JoyClearBC(&GBPadConfig[x]);

 for(x = 0; x < 9; x++)
  JoyClearBC(&LynxPadConfig[x]);

 for(x = 0; x < 9; x++)
  JoyClearBC(&NGPPadConfig[x]);

 for(x = 0; x < 5; x++)
  for(y = 0; y < 8; y++)
  JoyClearBC(&PCEPadConfig[x][y]);
}
