/* FCE Ultra - NES/Famicom Emulator
 *
 * Copyright notice for this file:
 *  Copyright (C) 2002 Xodnizel
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <QMessageBox>
#include <QFileDialog>
#include <QInputDialog>
#include <QApplication>

#include "Qt/main.h"
#include "Qt/dface.h"
#include "Qt/input.h"
#include "Qt/config.h"
#include "Qt/fceuWrapper.h"
#include "Qt/ConsoleWindow.h"
#include "Qt/ConsoleUtilities.h"
#include "Qt/CheatsConf.h"
#include "Qt/FamilyKeyboard.h"
#include "Qt/TasEditor/TasEditorWindow.h"

#include "Qt/sdl.h"
#include "Qt/sdl-video.h"
#include "Qt/sdl-joystick.h"

#include "common/cheat.h"
#include "../../movie.h"
#include "../../fceu.h"
#include "../../driver.h"
#include "../../state.h"
#include "../../utils/xstring.h"
#ifdef _S9XLUA_H
#include "../../fceulua.h"
#endif

#include <cstring>
#include <cstdio>

/** GLOBALS **/
int NoWaiting = 0;
extern Config *g_config;
extern bool bindSavestate, frameAdvanceLagSkip, lagCounterDisplay;
unsigned int frameAdvHoldTimer = 0;

/* UsrInputType[] is user-specified.  CurInputType[] is current
        (game loading can override user settings)
*/
static int UsrInputType[NUM_INPUT_DEVICES] = {SI_GAMEPAD, SI_GAMEPAD, SI_NONE};
static int CurInputType[NUM_INPUT_DEVICES] = {SI_GAMEPAD, SI_GAMEPAD, SI_NONE};
static int cspec = 0;
static int buttonConfigInProgress = 0;

extern int gametype;
static int DTestButton(ButtConfig *bc, bool isFKB = false);

//std::list<gamepad_function_key_t *> gpKeySeqList;

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

/**
 * Parse game information and configure the input devices accordingly.
 */
void ParseGIInput(FCEUGI *gi)
{
	gametype = gi->type;

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

	if (gi->input[0] >= 0)
	{
		CurInputType[0] = gi->input[0];
	}
	if (gi->input[1] >= 0)
	{
		CurInputType[1] = gi->input[1];
	}
	if (gi->inputfc >= 0)
	{
		CurInputType[2] = gi->inputfc;
	}
	cspec = gi->cspecial;
}

int getInputSelection(int port, int *cur, int *usr)
{
	if ((port >= 0) && (port < 3))
	{
		if (cur)
		{
			*cur = CurInputType[port];
		}
		if (usr)
		{
			*usr = UsrInputType[port];
		}
	}
	return 0;
}

static uint8 QuizKingData = 0;
static uint8 HyperShotData = 0;
static uint32 MahjongData = 0;
static uint32 FTrainerData = 0;
static uint8 TopRiderData = 0;
static uint8 BWorldData[1 + 13 + 1];

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);

static uint32 JSreturn = 0;

#include "keyscan.h"
static uint8_t g_keyState[SDL_NUM_SCANCODES];
static int keyModifier = 0;
//static int DIPS = 0;

//static uint8 keyonce[SDL_NUM_SCANCODES];
//#define KEY(__a) g_keyState[MKK(__a)]

int getKeyState(int k)
{
	k = SDL_GetScancodeFromKey(k);
	if ((k >= 0) && (k < SDL_NUM_SCANCODES))
	{
		return g_keyState[k];
	}
	return 0;
}

const uint8_t *QtSDL_getKeyboardState( int *bufSize )
{
	if (bufSize != nullptr)
	{
		*bufSize = SDL_NUM_SCANCODES;
	}
	return g_keyState;
}

//static int
//_keyonly(int a)
//{
//	int sc;
//
//	if (a < 0)
//	{
//		return 0;
//	}
//
//	sc = SDL_GetScancodeFromKey(a);
//
//	// check for valid key
//	if (sc >= SDL_NUM_SCANCODES || sc < 0)
//	{
//		return 0;
//	}
//
//	if (g_keyState[sc])
//	{
//		if (!keyonce[sc])
//		{
//			keyonce[sc] = 1;
//			return 1;
//		}
//	}
//	else
//	{
//		keyonce[sc] = 0;
//	}
//	return 0;
//}

uint32 GetGamepadPressedImmediate(void)
{
	//printf("JSreturn: 0x%08X\n", JSreturn);
	return JSreturn;
}

#define keyonly(__a) _keyonly(MKK(__a))

static bool g_fkbEnabled = false;

// this function loads the sdl hotkeys from the config file into the
// global scope.  this elimates the need for accessing the config file

class hotkey_t Hotkeys[HK_MAX];

hotkey_t::hotkey_t(void)
{
	sdl.value = 0;
	sdl.modifier = 0;
	qkey.value = 0;
	qkey.modifier = 0;
	prevState = 0;
	shortcut = nullptr;
	act = nullptr;
	configName = "";
}

int hotkey_t::init(QWidget *parent)
{
	std::string keyText;
	std::string prefix = "SDL.Hotkeys.";

	g_config->getOption(prefix + configName, &keyText);

	//printf("Initializing: '%s' = '%s'\n", configName, keyText.c_str() );

	shortcut = new QShortcut(QKeySequence(QString::fromStdString(keyText)), parent);

	//printf("ShortCut: '%s' = '%s'\n", configName, shortcut->key().toString().toLocal8Bit().constData() );

	conv2SDL();
	return 0;
}

int hotkey_t::readConfig(void)
{
	std::string keyText;
	std::string prefix = "SDL.Hotkeys.";

	g_config->getOption(prefix + configName, &keyText);

	//printf("Config: '%s' = '%s'\n", configName, keyText.c_str() );
	keySeq = QKeySequence( QString::fromStdString(keyText) );

	if (shortcut)
	{
		shortcut->setKey(QString::fromStdString(keyText));

		//printf("ShortCut: '%s' = '%s'\n", configName, shortcut->key().toString().toLocal8Bit().constData() );

		if (act)
		{
			act->setText(actText + "\t" + shortcut->key().toString());
		}
	}

	conv2SDL();
	return 0;
}

void hotkey_t::conv2SDL(void)
{
	if (shortcut == nullptr)
		return;

#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
	qkey.value    = shortcut->key()[0].key();
	qkey.modifier = shortcut->key()[0].keyboardModifiers();

	SDL_Keycode k = convQtKey2SDLKeyCode(shortcut->key()[0].key());
	SDL_Keymod m = convQtKey2SDLModifier(shortcut->key()[0].keyboardModifiers());
#else
	qkey.value    = (Qt::Key)(shortcut->key()[0] & 0x01FFFFFF);
	qkey.modifier = (Qt::KeyboardModifier)(shortcut->key()[0] & 0xFE000000);

	SDL_Keycode k = convQtKey2SDLKeyCode((Qt::Key)(shortcut->key()[0] & 0x01FFFFFF));
	SDL_Keymod m = convQtKey2SDLModifier((Qt::KeyboardModifier)(shortcut->key()[0] & 0xFE000000));
#endif

	//printf("Key: '%s'  0x%08x\n", shortcut->key().toString().toLocal8Bit().constData(), shortcut->key()[0] );

	sdl.value = k;
	sdl.modifier = m;

	//printf("Key: SDL: '%s' \n", SDL_GetKeyName(sdl.value) );
}

void hotkey_t::setConfigName(const char *cName)
{
	configName = cName;
}

void hotkey_t::setAction(QAction *actIn)
{
	act = actIn;

	actText = act->text();

	act->setText(actText + "\t" + shortcut->key().toString());
}

QShortcut *hotkey_t::getShortcut(void)
{
	return shortcut;
}

const char *hotkey_t::getConfigName(void)
{
	return configName;
}

int hotkey_t::getString(char *s)
{
	s[0] = 0;

	if (shortcut)
	{
		strcpy(s, shortcut->key().toString().toLocal8Bit().constData());
	}
	//if ( sdl.modifier != 0 )
	//{
	//	if ( sdl.modifier & (KMOD_LSHIFT | KMOD_RSHIFT) )
	//	{
	//		strcat( s, "Shift+" );
	//	}

	//	if ( sdl.modifier & (KMOD_LALT | KMOD_RALT) )
	//	{
	//		strcat( s, "Alt+" );
	//	}

	//	if ( sdl.modifier & (KMOD_LCTRL | KMOD_RCTRL) )
	//	{
	//		strcat( s, "Ctrl+" );
	//	}
	//}

	//strcat( s, SDL_GetKeyName(sdl.value) );

	return 0;
}

int hotkey_t::getState(void)
{
	int k;

	if (sdl.modifier != 0)
	{
		if (sdl.modifier & (KMOD_LSHIFT | KMOD_RSHIFT))
		{
			if (!g_keyState[SDL_SCANCODE_LSHIFT] && !g_keyState[SDL_SCANCODE_RSHIFT])
			{
				return 0;
			}
		}

		if (sdl.modifier & (KMOD_LALT | KMOD_RALT))
		{
			if (!g_keyState[SDL_SCANCODE_LALT] && !g_keyState[SDL_SCANCODE_RALT])
			{
				return 0;
			}
		}

		if (sdl.modifier & (KMOD_LCTRL | KMOD_RCTRL))
		{
			if (!g_keyState[SDL_SCANCODE_LCTRL] && !g_keyState[SDL_SCANCODE_RCTRL])
			{
				return 0;
			}
		}
	}
	else
	{
		if (keyModifier != 0)
		{
			return 0;
		}
	}

	k = SDL_GetScancodeFromKey(sdl.value);
	if ((k >= 0) && (k < SDL_NUM_SCANCODES))
	{
		return g_keyState[k];
	}
	return 0;
}

int hotkey_t::getRisingEdge(void)
{
	if (sdl.value < 0)
	{
		return 0;
	}

	if (getState())
	{
		if (!prevState)
		{
			prevState = 1;
			return 1;
		}
	}
	else
	{
		prevState = 0;
	}
	return 0;
}

void hotkey_t::setModifierFromString(const char *s)
{
	int i, j;
	char id[128];

	i = 0;
	j = 0;
	sdl.modifier = 0;

	while (s[i] != 0)
	{
		j = 0;
		while (isalnum(s[i]) || (s[i] == '_'))
		{
			id[j] = tolower(s[i]);
			j++;
			i++;
		}
		id[j] = 0;

		if (j == 0)
			break;

		if (strcmp(id, "ctrl") == 0)
		{
			sdl.modifier |= (KMOD_LCTRL | KMOD_RCTRL);
		}
		else if (strcmp(id, "alt") == 0)
		{
			sdl.modifier |= (KMOD_LALT | KMOD_RALT);
		}
		else if (strcmp(id, "shift") == 0)
		{
			sdl.modifier |= (KMOD_LSHIFT | KMOD_RSHIFT);
		}

		if ((s[i] == '+') || (s[i] == '|'))
		{
			i++;
		}
		else
		{
			break;
		}
	}
}

// on every cycle of keyboardinput()
void setHotKeys(void)
{
	for (int i = 0; i < HK_MAX; i++)
	{
		Hotkeys[i].readConfig();
	}

	if ( tasWin != NULL )
	{
		tasWin->initHotKeys();
	}
	return;
}

gamepad_function_key_t::gamepad_function_key_t(void)
{
	for (int i = 0; i < 2; i++)
	{
		keySeq[i].key = 0;
		keySeq[i].modifier = Qt::NoModifier;
		keyRelReq[i] = 0;
	}
	for (int i = 0; i < 2; i++)
	{
		hk[i] = -1;
	}
	for (int i = 0; i < 2; i++)
	{
		bmap[i].ButtType = -1;
		bmap[i].DeviceNum = -1;
		bmap[i].ButtonNum = -1;
		bmap[i].state = 0;
	}
}

gamepad_function_key_t::~gamepad_function_key_t(void)
{
}

void gamepad_function_key_t::sendKeyPressEvent(int idx)
{
	bool hasShortcut = false;

	// If the hot key has a shortcut associated with it, 
	// activate shortcut directly instead of attempting to send key sequence events.
	if ( (hk[idx] >= 0) && (hk[idx] < HK_MAX) )
	{
		QShortcut *s = Hotkeys[ hk[idx] ].getShortcut();

		if ( s && s->isEnabled() )
		{
			emit s->activated();
			hasShortcut = true;
		}
	}

	if ( !hasShortcut && (keySeq[idx].key > 0) )
	{
		QKeyEvent *k = new QKeyEvent(QEvent::KeyPress, keySeq[idx].key, (Qt::KeyboardModifiers)keySeq[idx].modifier);

		qApp->postEvent((QObject *)consoleWindow, (QEvent *)k);

		keyRelReq[idx] = 1;
	}
}

void gamepad_function_key_t::sendKeyReleaseEvent(int idx)
{
	if ( keyRelReq[idx] )
	{
		QKeyEvent *k = new QKeyEvent(QEvent::KeyRelease, keySeq[idx].key, (Qt::KeyboardModifiers)keySeq[idx].modifier);

		qApp->postEvent((QObject *)consoleWindow, (QEvent *)k);

		keyRelReq[idx] = 0;
	}
}

void gamepad_function_key_t::updateStatus(void)
{
	int state_lp[2], state[2];

	state_lp[0] = bmap[0].state;
	state_lp[1] = bmap[1].state;

	state[0] = DTestButton(&bmap[0]);
	state[1] = DTestButton(&bmap[1]);

	if ((bmap[0].ButtonNum >= 0) && (bmap[1].ButtonNum >= 0))
	{
		int s, lp;

		s = state[0] && state[1];
		lp = state_lp[0] && state_lp[1];

		if (s && !lp)
		{
			sendKeyPressEvent(0);
		}
		else if (!s && lp)
		{
			sendKeyReleaseEvent(0);
			sendKeyPressEvent(1);
			sendKeyReleaseEvent(1);
		}
	}
	else if (bmap[1].ButtonNum >= 0)
	{
		if (state[1] && !state_lp[1])
		{
			sendKeyPressEvent(0);
		}
		else if (!state[1] && state_lp[1])
		{
			sendKeyReleaseEvent(0);
			sendKeyPressEvent(1);
			sendKeyReleaseEvent(1);
		}
	}
}

/***
  * This function is a wrapper for FCEUI_ToggleEmulationPause that handles
  * releasing/capturing mouse pointer during pause toggles
  * */
void TogglePause(void)
{
	FCEUI_ToggleEmulationPause();

	int no_cursor;
	g_config->getOption("SDL.NoFullscreenCursor", &no_cursor);
	int fullscreen;
	g_config->getOption("SDL.Fullscreen", &fullscreen);

	return;
}

/*** 
 * This function opens a file chooser dialog and returns the filename the 
 * user selected.
 * */
static std::string GetFilename(const char *title, int mode, const char *filter)
{
	int ret, useNativeFileDialogVal;
	QFileDialog dialog(consoleWindow, title);
	std::string initPath;
	QList<QUrl> urls;

	//if (FCEUI_EmulationPaused () == 0)
	//	FCEUI_ToggleEmulationPause ();
	std::string fname = "";

	urls << QUrl::fromLocalFile(QDir::rootPath());
	urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first());
	urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::DesktopLocation).first());
	urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::DownloadLocation).first());
	urls << QUrl::fromLocalFile(QDir(FCEUI_GetBaseDirectory()).absolutePath());

	initPath.assign(FCEUI_GetBaseDirectory());

	switch (mode)
	{
	case 0: // Save State
		dialog.setLabelText(QFileDialog::Accept, dialog.tr("Save"));
		dialog.setFileMode(QFileDialog::AnyFile);
		initPath += "/fcs";
		break;
	default:
	case 1: // Load State
		dialog.setLabelText(QFileDialog::Accept, dialog.tr("Load"));
		dialog.setFileMode(QFileDialog::ExistingFile);
		initPath += "/fcs";
		break;
	case 2: // Record Movie To
		dialog.setLabelText(QFileDialog::Accept, dialog.tr("Record"));
		dialog.setFileMode(QFileDialog::AnyFile);
		initPath += "/movies";
		break;
	case 3: // Load Lua Script
		dialog.setLabelText(QFileDialog::Accept, dialog.tr("Load"));
		dialog.setFileMode(QFileDialog::ExistingFile);
		//initPath += "/fcs";
		break;
	}
	dialog.setFilter(QDir::AllEntries | QDir::AllDirs | QDir::Hidden);
	dialog.setDirectory(dialog.tr(initPath.c_str()));

	// Check config option to use native file dialog or not
	g_config->getOption("SDL.UseNativeFileDialog", &useNativeFileDialogVal);

	dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal);
	dialog.setSidebarUrls(urls);

	ret = dialog.exec();

	if (ret)
	{
		QStringList fileList;
		fileList = dialog.selectedFiles();

		if (fileList.size() > 0)
		{
			fname = fileList[0].toLocal8Bit().constData();
		}
	}

	//FCEUI_ToggleEmulationPause ();
	return fname;
}

/**
* Lets the user start a new .fm2 movie file
**/
void FCEUD_MovieRecordTo(void)
{
	//bool ok = false;
	//std::string fname = GetFilename("Save FM2 movie for recording", 2, "FM2 movies|*.fm2");
	//if (!fname.size())
	//{
	//	return;													// no filename selected, quit the whole thing
	//}
	//std::string s = QInputDialog::getText( consoleWindow, QObject::tr("Movie Recording"), 
	//			QObject::tr("Enter Author Name"), QLineEdit::Normal, QObject::tr(""), &ok ).toLocal8Bit().constData();

	//std::wstring author (s.begin (), s.end ());

	//FCEUI_SaveMovie(fname.c_str(), MOVIE_FLAG_FROM_POWERON, author);
	if ( consoleWindow )
	{
		consoleWindow->recordMovie();
	}
}

/**
* Lets the user save a savestate to a specific file
**/
void FCEUD_SaveStateAs(void)
{
	std::string fname = GetFilename("Save State As...", 0, "Save States|*.fc0");
	if (!fname.size())
		return; // no filename selected, quit the whole thing

	FCEUI_SaveState(fname.c_str());
}

/**
* Lets the user load a savestate from a specific file
*/
void FCEUD_LoadStateFrom(void)
{
	std::string fname = GetFilename("Load State From...", 1, "Save States|*.fc?");
	if (!fname.size())
		return; // no filename selected, quit the whole thing

	FCEUI_LoadState(fname.c_str());
}

/**
* Hook for transformer board
*/
unsigned int *GetKeyboard(void)
{
	int size = 256;

	Uint8 *keystate = (Uint8 *)SDL_GetKeyboardState(&size);

	return (unsigned int *)(keystate);
}

static void FKB_CheckShortcutConflicts(void)
{
	bool fkbActv;
	QShortcut *shortcut;

	fkbActv = g_fkbEnabled && (CurInputType[2] == SIFC_FKB);

	for (int i = 0; i < HK_MAX; i++)
	{
		shortcut = Hotkeys[i].getShortcut();

		if (i == HK_FKB_ENABLE)
		{
			if (shortcut)
			{
				shortcut->setEnabled(true);
			}
			continue;
		}

		if (shortcut)
		{
			shortcut->setEnabled(!fkbActv);
		}
	}
}

void toggleFamilyKeyboardFunc(void)
{
	if (CurInputType[2] == SIFC_FKB)
	{
		g_fkbEnabled = !g_fkbEnabled;

		FCEUI_DispMessage("Family Keyboard %sabled.", 0,
						  g_fkbEnabled ? "En" : "Dis");
	}
	else
	{
		FCEUI_DispMessage("Family Keyboard Not Active", 0);
		g_fkbEnabled = false;
	}
	FKB_CheckShortcutConflicts();
}

bool isFamilyKeyboardActv(void)
{
	return ((CurInputType[2] == SIFC_FKB) && g_fkbEnabled);
}

/**
 * Parse keyboard commands and execute accordingly.
 */
static void KeyboardCommands(void)
{
	// get the keyboard input

	// check if the family keyboard is enabled
	if (CurInputType[2] == SIFC_FKB)
	{
		if (g_fkbEnabled)
		{
			return;
		}
	}

	//if (g_keyState[SDL_SCANCODE_LSHIFT] || g_keyState[SDL_SCANCODE_RSHIFT])
	//{
	//	is_shift = 1;
	//}
	//else
	//{
	//	is_shift = 0;
	//}

	//if (g_keyState[SDL_SCANCODE_LALT] || g_keyState[SDL_SCANCODE_RALT])
	//{
	//	is_alt = 1;
	//}
	//else
	//{
	//	is_alt = 0;
	//}

	//if ( Hotkeys[HK_TOGGLE_BG].getRisingEdge() )
	//{
	//	bool fgOn, bgOn;

	//	FCEUI_GetRenderPlanes( fgOn, bgOn );

	//	FCEUI_SetRenderPlanes( fgOn, !bgOn );
	//}

	// Alt-Enter to toggle full-screen
	// This is already handled by Qt Menu Actions
	// So only process if menu is hidden or disabled.
	//if ( is_alt )
	//{
	//	if (keyonly (ENTER))
	//	{
	//		if ( consoleWindow )
	//		{
	//			if ( !consoleWindow->menuBar()->isVisible() )
	//			{
	//				consoleWindow->toggleFullscreen();
	//			}
	//		}
	//	}
	//}

	// Alt-M to toggle Main Menu Visibility
	//if ( is_alt )
	//{
	//	if (keyonly (SLASH))
	//	{
	//		if ( consoleWindow )
	//		{
	//			consoleWindow->toggleMenuVis();
	//		}
	//	}
	//}

	// Toggle Movie auto-backup
	//if ( is_shift )
	//{
	//	if (keyonly (M))
	//	{
	//		autoMovieBackup ^= 1;
	//		FCEUI_DispMessage ("Automatic movie backup %sabled.", 0,
	//			 autoMovieBackup ? "en" : "dis");
	//	}
	//}

	//if ( is_alt )
	//{
	//	// Start recording an FM2 movie on Alt+R
	//	if (keyonly (R))
	//	{
	//		FCEUD_MovieRecordTo ();
	//	}
	//	// Save a state from a file
	//	if (keyonly (S))
	//	{
	//		FCEUD_SaveStateAs ();
	//	}
	//	// Load a state from a file
	//	if (keyonly (L))
	//	{
	//		FCEUD_LoadStateFrom ();
	//	}
	//}

	// Famicom disk-system games
	//	if (gametype == GIT_FDS)
	//	{
	//		if ( Hotkeys[HK_FDS_SELECT].getRisingEdge() )
	//		{
	//			FCEUI_FDSSelect ();
	//		}
	//		if ( Hotkeys[HK_FDS_EJECT].getRisingEdge() )
	//		{
	//			FCEUI_FDSInsert ();
	//		}
	//	}
	//
	//	if ( Hotkeys[HK_SCREENSHOT].getRisingEdge() )
	//	{
	//		FCEUI_SaveSnapshot ();
	//	}

	// if not NES Sound Format
	//	if (gametype != GIT_NSF)
	//	{
	//		if ( Hotkeys[HK_CHEAT_MENU].getRisingEdge() )
	//		{
	//			openCheatDialog( consoleWindow );
	//		}
	//
	//		// f5 (default) save key, hold shift to save movie
	//		if ( Hotkeys[HK_SAVE_STATE].getRisingEdge() )
	//		{
	//			if (is_shift)
	//			{
	//				std::string movie_fname = FCEU_MakeFName (FCEUMKF_MOVIE, 0, 0);
	//				FCEUI_printf ("Recording movie to %s\n", movie_fname.c_str() );
	//				FCEUI_SaveMovie(movie_fname.c_str() , MOVIE_FLAG_NONE, L"");
	//			}
	//			else
	//			{
	//				FCEUI_SaveState (NULL);
	//			}
	//		}
	//
	//		// f7 to load state, Shift-f7 to load movie
	//		if ( Hotkeys[HK_LOAD_STATE].getRisingEdge() )
	//		{
	//			if (is_shift)
	//			{
	//				FCEUI_StopMovie ();
	//				std::string fname;
	//				fname =
	//				GetFilename ("Open FM2 movie for playback...", false,
	//								"FM2 movies|*.fm2");
	//				if (fname != "")
	//				{
	//					if (fname.find (".fm2") != std::string::npos
	//					|| fname.find (".fm3") != std::string::npos)
	//					{
	//						FCEUI_printf ("Playing back movie located at %s\n",
	//										fname.c_str ());
	//						FCEUI_LoadMovie (fname.c_str (), false, false);
	//					}
	//					else
	//					{
	//						FCEUI_printf
	//							("Only .fm2 and .fm3 movies are supported.\n");
	//					}
	//				}
	//			}
	//			else
	//			{
	//				FCEUI_LoadState(NULL);
	//			}
	//		}
	//	}

	//if ( Hotkeys[HK_DECREASE_SPEED].getRisingEdge() )
	//{
	//	DecreaseEmulationSpeed();
	//}

	//if ( Hotkeys[HK_INCREASE_SPEED].getRisingEdge() )
	//{
	//	IncreaseEmulationSpeed ();
	//}

	//if ( Hotkeys[HK_TOGGLE_FRAME_DISPLAY].getRisingEdge() )
	//{
	//	FCEUI_MovieToggleFrameDisplay ();
	//}

	//if ( Hotkeys[HK_TOGGLE_INPUT_DISPLAY].getRisingEdge() )
	//{
	//	FCEUI_ToggleInputDisplay ();
	//	extern int input_display;
	//	g_config->setOption ("SDL.InputDisplay", input_display);
	//}

	//if ( Hotkeys[HK_MOVIE_TOGGLE_RW].getRisingEdge() )
	//{
	//	FCEUI_SetMovieToggleReadOnly (!FCEUI_GetMovieToggleReadOnly ());
	//}

#ifdef CREATE_AVI
	if (Hotkeys[HK_MUTE_CAPTURE].getRisingEdge())
	{
		extern int mutecapture;
		mutecapture ^= 1;
	}
#endif

	//if ( Hotkeys[HK_PAUSE].getRisingEdge() )
	//{
	//	//FCEUI_ToggleEmulationPause();
	//	// use the wrapper function instead of the fceui function directly
	//	// so we can handle cursor grabbage
	//	TogglePause ();
	//}

	// Toggle throttling
	if (Hotkeys[HK_TURBO].getState())
	{
		NoWaiting |= 0x01;
	}
	else
	{
		NoWaiting &= 0x02;
		//printf("NoWaiting: 0x%04x\n", NoWaiting );
	}

	static bool frameAdvancing = false;
	if (Hotkeys[HK_FRAME_ADVANCE].getState())
	{
		if (frameAdvancing == false)
		{
			frameAdvHoldTimer = 0;
			FCEUI_FrameAdvance();
			frameAdvancing = true;
			//printf("Frame Advance Start\n");
		}
		if ( consoleWindow )
		{
			frameAdvHoldTimer += consoleWindow->getPeriodicInterval();
		}
	}
	else
	{
		if (frameAdvancing)
		{
			FCEUI_FrameAdvanceEnd();
			frameAdvancing = false;
			frameAdvHoldTimer = 0;
			//printf("Frame Advance End\n");
		}
	}

	//if ( Hotkeys[HK_RESET].getRisingEdge() )
	//{
	//	FCEUI_ResetNES ();
	//}
	//if( Hotkeys[HK_POWER].getRisingEdge() )
	//{
	//    FCEUI_PowerNES();
	//}
	//	if ( Hotkeys[HK_QUIT].getRisingEdge() )
	//	{
	//		CloseGame();
	//		FCEUI_Kill();
	//		SDL_Quit();
	//		exit(0);
	//	}
	//	else
	//#ifdef _S9XLUA_H
	//	if ( Hotkeys[HK_LOAD_LUA].getRisingEdge() )
	//	{
	//		std::string fname;
	//		fname = GetFilename ("Open LUA script...", 3, "Lua scripts|*.lua");
	//		if (fname != "")
	//		FCEU_LoadLuaCode (fname.c_str ());
	//	}
	//#endif

	//for (int i = 0; i < 10; i++)
	//{
	//	if ( Hotkeys[HK_SELECT_STATE_0 + i].getRisingEdge() )
	//	{
	//		FCEUI_SelectState (i, 1);
	//	}
	//}

	//if ( Hotkeys[HK_SELECT_STATE_NEXT].getRisingEdge() )
	//{
	//	FCEUI_SelectStateNext (1);
	//}

	//if ( Hotkeys[HK_SELECT_STATE_PREV].getRisingEdge() )
	//{
	//	FCEUI_SelectStateNext (-1);
	//}

	//if ( Hotkeys[HK_BIND_STATE].getRisingEdge() )
	//{
	//	bindSavestate ^= 1;
	//	FCEUI_DispMessage ("Savestate binding to movie %sabled.", 0,
	//	bindSavestate ? "en" : "dis");
	//}

	//if ( Hotkeys[HK_FA_LAG_SKIP].getRisingEdge() )
	//{
	//	frameAdvanceLagSkip ^= 1;
	//	FCEUI_DispMessage ("Skipping lag in Frame Advance %sabled.", 0,
	//	frameAdvanceLagSkip ? "en" : "dis");
	//}

	//if ( Hotkeys[HK_LAG_COUNTER_DISPLAY].getRisingEdge() )
	//{
	//	lagCounterDisplay ^= 1;
	//}

	//if ( Hotkeys[HK_TOGGLE_SUBTITLE].getRisingEdge() )
	//{
	//	movieSubtitles = !movieSubtitles;
	//	FCEUI_DispMessage ("Movie subtitles o%s.", 0,
	//	movieSubtitles ? "n" : "ff");
	//}

	//if ( Hotkeys[HK_VOLUME_DOWN].getRisingEdge() )
	//{
	//	FCEUD_SoundVolumeAdjust(-1);
	//}

	//if ( Hotkeys[HK_VOLUME_UP].getRisingEdge() )
	//{
	//	FCEUD_SoundVolumeAdjust(1);
	//}

	// VS Unisystem games
	//	if (gametype == GIT_VSUNI)
	//	{
	//		// insert coin
	//		if ( Hotkeys[HK_VS_INSERT_COIN].getRisingEdge() )
	//		{
	//			FCEUI_VSUniCoin ();
	//		}
	//
	//		// toggle dipswitch display
	//		if ( Hotkeys[HK_VS_TOGGLE_DIPSWITCH].getRisingEdge() )
	//		{
	//			DIPS ^= 1;
	//			FCEUI_VSUniToggleDIPView ();
	//		}
	//		if (!(DIPS & 1))
	//			goto DIPSless;
	//
	//		// toggle the various dipswitches
	//		for(int i=1; i<=8;i++)
	//		{
	//			if(keyonly(i))
	//				FCEUI_VSUniToggleDIP(i-1);
	//		}
	//	}
	//	else
	//	{
	//		static uint8 bbuf[32];
	//		static int bbuft;
	//		static int barcoder = 0;
	//
	//		if (keyonly (H))
	//			FCEUI_NTSCSELHUE ();
	//		if (keyonly (T))
	//			FCEUI_NTSCSELTINT ();
	//
	//		if (Hotkeys[HK_DECREASE_SPEED].getRisingEdge())
	//		{
	//			FCEUI_NTSCDEC ();
	//		}
	//		if (Hotkeys[HK_INCREASE_SPEED].getRisingEdge())
	//		{
	//			FCEUI_NTSCINC ();
	//		}
	//
	//		if ((CurInputType[2] == SIFC_BWORLD) || (cspec == SIS_DATACH))
	//		{
	//			if (keyonly (F8))
	//			{
	//				barcoder ^= 1;
	//				if (!barcoder)
	//				{
	//					if (CurInputType[2] == SIFC_BWORLD)
	//					{
	//						strcpy ((char *) &BWorldData[1], (char *) bbuf);
	//						BWorldData[0] = 1;
	//					}
	//					else
	//					{
	//						FCEUI_DatachSet (bbuf);
	//					}
	//					FCEUI_DispMessage ("Barcode Entered", 0);
	//				}
	//				else
	//				{
	//					bbuft = 0;
	//					FCEUI_DispMessage ("Enter Barcode", 0);
	//				}
	//			}
	//		}
	//		else
	//		{
	//			barcoder = 0;
	//		}
	/*
#define SSM(x)
do {                                              \
	if(barcoder) {                                \
		if(bbuft < 13) {                          \
			bbuf[bbuft++] = '0' + x;              \
			bbuf[bbuft] = 0;                      \
		}                                         \
		FCEUI_DispMessage("Barcode: %s",0, bbuf); \
	}                                             \
} while(0)

		DIPSless:
		for(int i=0; i<10;i++)
		{
			if (keyonly (i))
				SSM (i);
		}
#undef SSM
*/
	//	}
}

/**
 * Return the state of the mouse buttons.  Input 'd' is an array of 3
 * integers that store <x, y, button state>.
 */
void GetMouseData(uint32 (&d)[3])
{
	uint32 t, b;
	double nx = 0.0, ny = 0.0;

	b = 0; // map mouse buttons

	if (consoleWindow->viewport_Interface)
	{
		consoleWindow->viewport_Interface->getNormalizedCursorPos(nx, ny);

		if (consoleWindow->viewport_Interface->getMouseButtonState(Qt::LeftButton))
		{
			b |= 0x01;
		}
		if (consoleWindow->viewport_Interface->getMouseButtonState(Qt::RightButton))
		{
			b |= 0x02;
		}
	}

	t = PtoV(nx, ny);
	d[2] = b;
	d[0] = t & 0xFFFF;
	d[1] = (t >> 16) & 0xFFFF;

	//printf("mouse %d %d %d\n", d[0], d[1], d[2]);
}

void GetMouseRelative(int32 (&d)[3])
{
	// converts absolute mouse positions to relative ones for input devices that require this

	// The windows version additionally in fullscreen will constantly return the mouse to center screen
	// after reading it, so that the user can endlessly keep moving the mouse.
	// The same should eventually be implemented here, but this version should minimally provide
	// the necessary relative input, piggybacking on the already implemented GetMouseData.

	static int cx = -1;
	static int cy = -1;

	uint32 md[3];
	GetMouseData(md);

	if (cx < 0 || cy < 0)
	{
		cx = md[0];
		cy = md[1];
	}

	int dx = md[0] - cx;
	int dy = md[1] - cy;

	d[0] = dx;
	d[1] = dy;
	d[2] = md[2]; // buttons
}

//static void checkKeyBoardState( int scanCode )
//{
//	printf("Key State is: %i \n", g_keyState[ scanCode ] );
//}
/**
 * Handles outstanding SDL events.
 */
void
pollEventsSDL()
{
	SDL_Event event;

	// loop, handling all pending events
	while (SDL_PollEvent(&event))
	{
		switch (event.type)
		{
		case SDL_QUIT:
			CloseGame();
			puts("Quit");
			break;
		case SDL_FCEU_HOTKEY_EVENT:
			switch (event.user.code)
			{
			case HK_PAUSE:
				TogglePause();
				break;
			default:
				FCEU_printf("Warning: unknown hotkey event %d\n",
							event.user.code);
			}
			break;
		case SDL_KEYDOWN:
		case SDL_KEYUP:
			//printf("SDL_Event.type: %i  Keysym: %i  ScanCode: %i\n",
			//		event.type, event.key.keysym.sym, event.key.keysym.scancode );

			#ifdef WIN32
			g_keyState[SDL_SCANCODE_LSHIFT] = win32GetKeyState( SDL_SCANCODE_LSHIFT );
			g_keyState[SDL_SCANCODE_RSHIFT] = win32GetKeyState( SDL_SCANCODE_RSHIFT );
			g_keyState[SDL_SCANCODE_LALT  ] = win32GetKeyState( SDL_SCANCODE_LALT   );
			g_keyState[SDL_SCANCODE_RALT  ] = win32GetKeyState( SDL_SCANCODE_RALT   );
			g_keyState[SDL_SCANCODE_LCTRL ] = win32GetKeyState( SDL_SCANCODE_LCTRL  );
			g_keyState[SDL_SCANCODE_RCTRL ] = win32GetKeyState( SDL_SCANCODE_RCTRL  );
			g_keyState[SDL_SCANCODE_LGUI  ] = win32GetKeyState( SDL_SCANCODE_LGUI   );
			g_keyState[SDL_SCANCODE_RGUI  ] = win32GetKeyState( SDL_SCANCODE_RGUI   );
			#endif

			keyModifier = event.key.keysym.mod;

			if ( (event.key.keysym.mod & KMOD_LSHIFT) == 0 )
			{
				g_keyState[SDL_SCANCODE_LSHIFT] = 0;
			}
			if ( (event.key.keysym.mod & KMOD_RSHIFT) == 0 )
			{
				g_keyState[SDL_SCANCODE_RSHIFT] = 0;
			}
			if ( (event.key.keysym.mod & KMOD_LALT) == 0 )
			{
				g_keyState[SDL_SCANCODE_LALT] = 0;
			}
			if ( (event.key.keysym.mod & KMOD_RALT) == 0 )
			{
				g_keyState[SDL_SCANCODE_RALT] = 0;
			}
			if ( (event.key.keysym.mod & KMOD_LCTRL) == 0 )
			{
				g_keyState[SDL_SCANCODE_LCTRL] = 0;
			}
			if ( (event.key.keysym.mod & KMOD_RCTRL) == 0 )
			{
				g_keyState[SDL_SCANCODE_RCTRL] = 0;
			}

			g_keyState[event.key.keysym.scancode] = (event.type == SDL_KEYDOWN) ? 1 : 0;

			KeyboardCommands();

			break;
		case SDL_JOYDEVICEADDED:
			{
				int devIdx = AddJoystick(event.jdevice.which);
				if (devIdx >= 0)
				{
					int newDeviceBehavior = 0;
					g_config->getOption("SDL.NewInputDeviceBehavior", &newDeviceBehavior);

					if (newDeviceBehavior == 1)
					{
						bool wasPaused = FCEUI_EmulationPaused() ? true : false;

						jsDev_t* jsDev = getJoystickDevice(devIdx);

						QString msg = "A new joystick/gamepad device has been detected.\n";

						if (jsDev != nullptr)
						{
							msg += QString("\nDevice ") + QString::number(devIdx) + QString(": ");
							msg += QString(jsDev->getName()) + "\n";
						}
						msg += "\nDo you wish to reload button bindings?";
						QMessageBox msgBox(QMessageBox::Question, QObject::tr("New Device Detected"), msg,
							QMessageBox::No | QMessageBox::Yes, consoleWindow);

						msgBox.setDefaultButton( QMessageBox::Yes );

						FCEUI_SetEmulationPaused( EMULATIONPAUSED_PAUSED );

						int answer = msgBox.exec();

						if ( answer == QMessageBox::Yes )
						{
							initGamepadBindings();
						}
						if (!wasPaused)
						{
							FCEUI_SetEmulationPaused(0);
						}
					}
					else if (newDeviceBehavior == 2)
					{
						initGamepadBindings();
					}
				}
			}
			break;
		case SDL_JOYDEVICEREMOVED:
			RemoveJoystick(event.jdevice.which);
			break;
		default:
			break;
		}
	}
	//SDL_PumpEvents();
}

/**
 *  Begin configuring the buttons by placing the video and joystick
 *  subsystems into a well-known state.  Button configuration really
 *  needs to be cleaned up after the new config system is in place.
 */
int ButtonConfigBegin()
{
	// initialize the joystick subsystem (if not already inited)
	InitJoysticks();

	buttonConfigInProgress = 1;

	return 1;
}

/**
 *  Finish configuring the buttons by reverting the video and joystick
 *  subsystems to their previous state.  Button configuration really
 *  needs to be cleaned up after the new config system is in place.
 */
void ButtonConfigEnd()
{
	buttonConfigInProgress = 0;
}

/**
 * Tests to see if a specified button is currently pressed.
 */
static int
DTestButton(ButtConfig *bc, bool isFKB)
{
	if (bc->ButtType == BUTTC_KEYBOARD)
	{
		bool ignoreKB = false;
		bool fkbActv  = g_fkbEnabled && (CurInputType[2] == SIFC_FKB);

		if (fkbActv)
		{
			ignoreKB = !isFKB;	
		}
		if (!ignoreKB && g_keyState[SDL_GetScancodeFromKey(bc->ButtonNum)])
		{
			bc->state = 1;
			return 1;
		}
		else
		{
			bc->state = 0;
		}
	}
	else if (bc->ButtType == BUTTC_JOYSTICK)
	{
		if (DTestButtonJoy(bc))
		{
			return 1;
		}
	}
	return 0;
}

#define MK(x)                     \
	{                             \
		BUTTC_KEYBOARD, 0, (x), 0 \
	}
//#define MK2(x1,x2)  {BUTTC_KEYBOARD,0,MKK(x1)}
#define MKZ()       \
	{               \
		0, 0, -1, 0 \
	}
#define GPZ()                      \
	{                              \
		MKZ(), MKZ(), MKZ(), MKZ() \
	}

//ButtConfig GamePadConfig[ GAMEPAD_NUM_DEVICES ][ GAMEPAD_NUM_BUTTONS ] =
//{
///* Gamepad 1 */
//	{MK (KP_3), MK (KP_2), MK (SLASH), MK (ENTER),
//	MK (w), MK (z), MK (a), MK (s), MKZ (), MKZ ()},
//
//	/* Gamepad 2 */
//	GPZ (),
//
//	/* Gamepad 3 */
//	GPZ (),
//
//	/* Gamepad 4 */
//	GPZ ()
//};

/**
 * Update the status of the gamepad input devices.
 */
static void
UpdateGamepad(void)
{
	// don't update during movie playback
	if (FCEUMOV_Mode(MOVIEMODE_PLAY))
	{
		return;
	}

	uint32 JS = 0;
	int x,c;
	int wg;
	bool fire;
	char btns[GAMEPAD_NUM_BUTTONS];

	int opposite_dirs;
	g_config->getOption("SDL.Input.EnableOppositeDirectionals", &opposite_dirs);

	// go through each of the four game pads
	for (wg = 0; wg < 4; wg++)
	{
		bool left = false;
		bool up = false;
		memset( btns, 0, sizeof(btns) );

		for (c = 0; c < GamePad_t::NUM_CONFIG; c++)
		{
			// a, b, select, start, up, down, left, right
			for (x = 0; x < 8; x++)
			{
				if (DTestButton(&GamePad[wg].bmap[c][x]))
				{
					btns[x] = 1;
					//printf("GamePad%i Button Hit: %i \n", wg, x );
					if (opposite_dirs == 0)
					{
						// test for left+right and up+down
						if (x == 4)
						{
							up = true;
						}
						if ((x == 5) && (up == true))
						{
							continue;
						}
						if (x == 6)
						{
							left = true;
						}
						if ((x == 7) && (left == true))
						{
							continue;
						}
					}
					JS |= (1 << x) << (wg << 3);
				}
			}

			int four_button_exit;
			g_config->getOption("SDL.ABStartSelectExit", &four_button_exit);
			// if a+b+start+select is pressed, exit
			if (four_button_exit && JS == 15)
			{
				FCEUI_printf("all buttons pressed, exiting\n");
				CloseGame();
				FCEUI_Kill();
				exit(0);
			}

			// rapid-fire a, rapid-fire b
			for (x = 0; x < 2; x++)
			{
				if (DTestButton(&GamePad[wg].bmap[c][8 + x]))
				{
					fire = GetAutoFireState(x);

					if (fire)
					{
						JS |= (1 << x) << (wg << 3);
					}
					btns[8+x] = 1;
				}
			}
		}

		for (x = 0; x < GAMEPAD_NUM_BUTTONS; x++)
		{
			GamePad[wg].bmapState[x] = btns[x];
		}
	}

	//  for(x=0;x<32;x+=8)      /* Now, test to see if anything weird(up+down at same time)
	//                     is happening, and correct */
	//  {
	//   if((JS & (0xC0<<x) ) == (0xC0<<x) ) JS&=~(0xC0<<x);
	//   if((JS & (0x30<<x) ) == (0x30<<x) ) JS&=~(0x30<<x);
	//  }

	JSreturn = JS;
}

static ButtConfig powerpadsc[2][12] = {
	{MK(SDLK_O), MK(SDLK_P), MK(SDLK_BRACKET_LEFT),
	 MK(SDLK_BRACKET_RIGHT), MK(SDLK_K), MK(SDLK_L), MK(SDLK_SEMICOLON),
	 MK(SDLK_APOSTROPHE),
	 MK(SDLK_M), MK(SDLK_COMMA), MK(SDLK_PERIOD), MK(SDLK_SLASH)},
	{MK(SDLK_O), MK(SDLK_P), MK(SDLK_BRACKET_LEFT),
	 MK(SDLK_BRACKET_RIGHT), MK(SDLK_K), MK(SDLK_L), MK(SDLK_SEMICOLON),
	 MK(SDLK_APOSTROPHE),
	 MK(SDLK_M), MK(SDLK_COMMA), MK(SDLK_PERIOD), MK(SDLK_SLASH)}};

static uint32 powerpadbuf[2] = {0, 0};

/**
 * Update the status of the power pad input device.
 */
static uint32
UpdatePPadData(int w)
{
	// don't update if a movie is playing
	if (FCEUMOV_Mode(MOVIEMODE_PLAY))
	{
		return 0;
	}

	uint32 r = 0;
	ButtConfig *ppadtsc = powerpadsc[w];
	int x;

	// update each of the 12 buttons
	for (x = 0; x < 12; x++)
	{
		if (DTestButton(&ppadtsc[x]))
		{
			r |= 1 << x;
		}
	}

	return r;
}

static uint32 MouseData[3] = {0, 0, 0};
static int32 MouseRelative[3] = {0, 0, 0};

static uint8 fkbkeys[0x48];

static void updateGamePadKeyMappings(void)
{
	std::list<gamepad_function_key_t *>::iterator it;

	for (int i=0; i<4; i++)
	{
		if (GamePad[i].gpKeySeqList.size() == 0)
		{
			continue;
		}

		for (it = GamePad[i].gpKeySeqList.begin(); it != GamePad[i].gpKeySeqList.end(); it++)
		{
			(*it)->updateStatus();
		}
	}
}

/**
 * Update all of the input devices required for the active game.
 */
void FCEUD_UpdateInput(void)
{
	int x;
	int t = 0;

	if (buttonConfigInProgress)
	{
		return;
	}

	updateGamePadKeyMappings();

	pollEventsSDL();
	KeyboardCommands();

	for (x = 0; x < 2; x++)
	{
		switch (CurInputType[x])
		{
		case SI_GAMEPAD:
		case SI_SNES:
			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;
		case SI_MOUSE:
		case SI_SNES_MOUSE:
			t |= 4;
			break;
		}
	}

	switch (CurInputType[2])
	{
	case SIFC_ARKANOID:
		t |= 2;
		break;
	case SIFC_SHADOW:
		t |= 2;
		break;
	case SIFC_FKB:
		if (g_fkbEnabled)
		{
			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();
	}

	// Don't get input when a movie is playing back
	if (!FCEUMOV_Mode(MOVIEMODE_PLAY))
	{
		if (t & 2)
		{
			GetMouseData(MouseData);
		}
		if (t & 4)
		{
			GetMouseRelative(MouseRelative);
		}
	}
}

void FCEUD_SetInput(bool fourscore, bool microphone, ESI port0, ESI port1,
					ESIFC fcexp)
{
	eoptions &= ~EO_FOURSCORE;
	if (fourscore)
	{ // Four Score emulation, only support gamepads, nothing else
		eoptions |= EO_FOURSCORE;
		CurInputType[0] = SI_GAMEPAD; // Controllers 1 and 3
		CurInputType[1] = SI_GAMEPAD; // Controllers 2 and 4
		CurInputType[2] = SIFC_NONE;  // No extension
	}
	else
	{
		// no Four Core emulation, check the config/movie file for controller types
		CurInputType[0] = port0;
		CurInputType[1] = port1;
		CurInputType[2] = fcexp;
	}
	if (CurInputType[2] != SIFC_FKB)
	{
		g_fkbEnabled = false;
	}

	replaceP2StartWithMicrophone = microphone;

	InitInputInterface();
}

/**
 * Initialize the input device interface between the emulation and the driver.
 */
void InitInputInterface()
{
	void *InputDPtr;

	int t;
	int x;
	int attrib;

	memset(g_keyState, 0, sizeof(g_keyState));

	for (t = 0, x = 0; x < 2; x++)
	{
		attrib = 0;
		InputDPtr = 0;

		switch (CurInputType[x])
		{
		case SI_POWERPADA:
		case SI_POWERPADB:
			InputDPtr = &powerpadbuf[x];
			break;
		case SI_GAMEPAD:
		case SI_SNES:
			InputDPtr = &JSreturn;
			break;
		case SI_ARKANOID:
			InputDPtr = MouseData;
			t |= 1;
			break;
		case SI_ZAPPER:
			InputDPtr = MouseData;
			t |= 1;
			attrib = 1;
			break;
		case SI_MOUSE:
		case SI_SNES_MOUSE:
			InputDPtr = MouseRelative;
			t |= 1;
			break;
		}
		FCEUI_SetInput(x, (ESI)CurInputType[x], InputDPtr, attrib);
	}

	attrib = 0;
	InputDPtr = 0;
	switch (CurInputType[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;
	}

	FCEUI_SetInputFC((ESIFC)CurInputType[2], InputDPtr, attrib);
	FCEUI_SetInputFourscore((eoptions & EO_FOURSCORE) != 0);
}

ButtConfig fkbmap[FAMILYKEYBOARD_NUM_BUTTONS] = {
	/*  0 */ MK(SDLK_F1), MK(SDLK_F2), MK(SDLK_F3), MK(SDLK_F4), MK(SDLK_F5), MK(SDLK_F6), MK(SDLK_F7), MK(SDLK_F8),
	/*  8 */ MK(SDLK_1), MK(SDLK_2), MK(SDLK_3), MK(SDLK_4), MK(SDLK_5), MK(SDLK_6), MK(SDLK_7), MK(SDLK_8), MK(SDLK_9),
	/* 17 */ MK(SDLK_0),
	/* 18 */ MK(SDLK_MINUS), MK(SDLK_EQUAL), MK(SDLK_BACKSLASH), MK(SDLK_BACKSPACE),
	/* 22 */ MK(SDLK_ESCAPE), MK(SDLK_Q), MK(SDLK_W), MK(SDLK_E), MK(SDLK_R), MK(SDLK_T), MK(SDLK_Y), MK(SDLK_U), MK(SDLK_I),
	/* 31 */ MK(SDLK_O),
	/* 32 */ MK(SDLK_P), MK(SDLK_GRAVE), MK(SDLK_BRACKET_LEFT), MK(SDLK_ENTER),
	/* 36 */ MK(SDLK_LEFTCONTROL), MK(SDLK_A), MK(SDLK_S), MK(SDLK_D), MK(SDLK_F), MK(SDLK_G), MK(SDLK_H), MK(SDLK_J),
	/* 44 */ MK(SDLK_K),
	/* 45 */ MK(SDLK_L), MK(SDLK_SEMICOLON), MK(SDLK_APOSTROPHE), MK(SDLK_BRACKET_RIGHT), MK(SDLK_INSERT),
	/* 50 */ MK(SDLK_LEFTSHIFT), MK(SDLK_Z), MK(SDLK_X), MK(SDLK_C), MK(SDLK_V), MK(SDLK_B), MK(SDLK_N), MK(SDLK_M),
	/* 58 */ MK(SDLK_COMMA),
	/* 59 */ MK(SDLK_PERIOD), MK(SDLK_SLASH), MK(SDLK_RIGHTALT), MK(SDLK_RIGHTSHIFT), MK(SDLK_LEFTALT),
	/* 64 */ MK(SDLK_SPACE),
	/* 65 */ MK(SDLK_DELETE), MK(SDLK_END), MK(SDLK_PAGEDOWN),
	/* 68 */ MK(SDLK_CURSORUP), MK(SDLK_CURSORLEFT), MK(SDLK_CURSORRIGHT), MK(SDLK_CURSORDOWN)};

/**
 * Update the status of the Family KeyBoard.
 */
static void UpdateFKB(void)
{
	int x;
	char leftShiftDown, vkeyDown;
	//static char lp[0x48];

	vkeyDown = getFamilyKeyboardVirtualKey(50);

	leftShiftDown = DTestButton(&fkbmap[50], true) || vkeyDown;

	for (x = 0; x < FAMILYKEYBOARD_NUM_BUTTONS; x++)
	{
		if ( leftShiftDown && (x == 62) )
		{	// Family BASIC appears to not like when both shift keys are pressed at the
			// same time. Since Qt key events do not differentiate between left and right
			// shift this GUI sets both left and right shift scancodes when a shift modifier
			// is detected. So to avoid having the FKB see both shift keys pressed at once,
			// always skip the right shift key here if the left key is already detected down.
			fkbkeys[x] = 0;
			continue;
		}

		vkeyDown = getFamilyKeyboardVirtualKey(x);

		if (DTestButton(&fkbmap[x], true) || vkeyDown)
		{
			fkbkeys[x] = 1;

			//if ( !lp[x] )
			//{
			//	printf("FKB Key %i Down\n", x );
			//}
		}
		else
		{
			fkbkeys[x] = 0;

			//if ( lp[x] )
			//{
			//	printf("FKB Key %i Up\n", x );
			//}
		}
		//lp[x] = fkbkeys[x];
	}
}

const uint8_t *getFamilyKeyboardState(void)
{
	return fkbkeys;
}

static ButtConfig HyperShotButtons[4] = {
	MK(SDLK_Q), MK(SDLK_W), MK(SDLK_E), MK(SDLK_R)};

/**
 * Update the status of the HyperShot input device.
 */
static void
UpdateHyperShot()
{
	int x;

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

static ButtConfig MahjongButtons[21] = {
	MK(SDLK_Q), MK(SDLK_W), MK(SDLK_E), MK(SDLK_R), MK(SDLK_T),
	MK(SDLK_A), MK(SDLK_S), MK(SDLK_D), MK(SDLK_F), MK(SDLK_G), MK(SDLK_H), MK(SDLK_J), MK(SDLK_K), MK(SDLK_L),
	MK(SDLK_Z), MK(SDLK_X), MK(SDLK_C), MK(SDLK_V), MK(SDLK_B), MK(SDLK_N), MK(SDLK_M)};

/**
 * Update the status of for the Mahjong input device.
 */
static void
UpdateMahjong()
{
	int x;

	MahjongData = 0;
	for (x = 0; x < 21; x++)
	{
		if (DTestButton(&MahjongButtons[x]))
		{
			MahjongData |= 1 << x;
		}
	}
}

static ButtConfig QuizKingButtons[6] = {
	MK(SDLK_Q), MK(SDLK_W), MK(SDLK_E), MK(SDLK_R), MK(SDLK_T), MK(SDLK_Y)};

/**
 * Update the status of the QuizKing input device.
 */
static void
UpdateQuizKing()
{
	int x;

	QuizKingData = 0;

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

static ButtConfig TopRiderButtons[8] = {
	MK(SDLK_Q), MK(SDLK_W), MK(SDLK_E), MK(SDLK_R), MK(SDLK_T), MK(SDLK_Y), MK(SDLK_U), MK(SDLK_I)};

/**
 * Update the status of the TopRider input device.
 */
static void
UpdateTopRider()
{
	int x;
	TopRiderData = 0;
	for (x = 0; x < 8; x++)
	{
		if (DTestButton(&TopRiderButtons[x]))
		{
			TopRiderData |= (1 << x);
		}
	}
}

static ButtConfig FTrainerButtons[12] = {
	MK(SDLK_O), MK(SDLK_P), MK(SDLK_BRACKET_LEFT),
	MK(SDLK_BRACKET_RIGHT), MK(SDLK_K), MK(SDLK_L), MK(SDLK_SEMICOLON),
	MK(SDLK_APOSTROPHE),
	MK(SDLK_M), MK(SDLK_COMMA), MK(SDLK_PERIOD), MK(SDLK_SLASH)};

/**
 * Update the status of the FTrainer input device.
 */
static void
UpdateFTrainer()
{
	int x;
	FTrainerData = 0;

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

/**
 * Get the display name of the key or joystick button mapped to a specific 
 * NES gamepad button.
 * @param bc the NES gamepad's button config
 * @param which the index of the button
 */
const char *ButtonName(const ButtConfig *bc)
{
	static char name[256];

	name[0] = 0;

	if (bc->ButtonNum == -1)
	{
		return name;
	}
	switch (bc->ButtType)
	{
	case BUTTC_KEYBOARD:
		return SDL_GetKeyName(bc->ButtonNum);
		break;
	case BUTTC_JOYSTICK:
	{
		int joyNum, inputNum;
		const char *inputType, *inputDirection;
		char direction[128] = "";

		joyNum = bc->DeviceNum;

		if (bc->ButtonNum & 0x8000)
		{
			inputType = "Axis";
			inputNum = bc->ButtonNum & 0x3FFF;
			inputDirection = bc->ButtonNum & 0x4000 ? "-" : "+";
		}
		else if (bc->ButtonNum & 0x2000)
		{
			int inputValue;

			inputType = "Hat";
			inputNum = (bc->ButtonNum >> 8) & 0x1F;
			inputValue = bc->ButtonNum & 0xF;

			if (inputValue & SDL_HAT_UP)
				strncat(direction, "Up ", sizeof(direction) - 1);
			if (inputValue & SDL_HAT_DOWN)
				strncat(direction, "Down ", sizeof(direction) - 1);
			if (inputValue & SDL_HAT_LEFT)
				strncat(direction, "Left ", sizeof(direction) - 1);
			if (inputValue & SDL_HAT_RIGHT)
				strncat(direction, "Right ", sizeof(direction) - 1);

			if (direction[0])
				inputDirection = direction;
			else
				inputDirection = "Center";
		}
		else
		{
			inputType = "Button";
			inputNum = bc->ButtonNum;
			inputDirection = "";
		}
		snprintf(name, sizeof(name), "js%i:%s%i%s", joyNum, inputType, inputNum, inputDirection);
	}
	break;
	}

	return name;
}

/**
 * Waits for a button input and returns the information as to which
 * button was pressed.  Used in button configuration.
 */
int DWaitButton(const uint8_t *text, ButtConfig *bc, int *buttonConfigStatus)
{
	SDL_Event event;
	static int32 LastAx[64][64];
	int x, y;
	int timeout_ms = 10000;

	if (text)
	{
		std::string title = "Press a key for ";
		title += (const char *)text;
		// TODO - SDL2
		//SDL_WM_SetCaption (title.c_str (), 0);
		puts((const char *)text);
	}

	for (x = 0; x < 64; x++)
	{
		for (y = 0; y < 64; y++)
		{
			LastAx[x][y] = 0x100000;
		}
	}

	// Purge all pending events, so that this next button press
	// will be the one we want.
	while (SDL_PollEvent(&event))
	{
	}

	while (1)
	{
		int done = 0;

		SDL_Delay(10);
		timeout_ms -= 10;

		if (timeout_ms <= 0)
		{
			break;
		}

		QCoreApplication::processEvents();

		while (SDL_PollEvent(&event))
		{
			done++;
			switch (event.type)
			{
			case SDL_KEYDOWN:
				//printf("SDL KeyDown:%i \n", event.key.keysym.sym );
				bc->ButtType = BUTTC_KEYBOARD;
				bc->DeviceNum = 0;
				bc->ButtonNum = event.key.keysym.sym;
				return (1);
			case SDL_JOYBUTTONDOWN:
				bc->ButtType = BUTTC_JOYSTICK;
				bc->DeviceNum = FindJoystickByInstanceID(event.jbutton.which);
				bc->ButtonNum = event.jbutton.button;
				return (1);
			case SDL_JOYHATMOTION:
				if (event.jhat.value == SDL_HAT_CENTERED)
					done--;
				else
				{
					bc->ButtType = BUTTC_JOYSTICK;
					bc->DeviceNum = FindJoystickByInstanceID(event.jhat.which);
					bc->ButtonNum =
						(0x2000 | ((event.jhat.hat & 0x1F) << 8) | event.jhat.value);
					return (1);
				}
				break;
			case SDL_JOYAXISMOTION:
				if (LastAx[event.jaxis.which][event.jaxis.axis] == 0x100000)
				{
					if (abs(event.jaxis.value) < 1000)
					{
						LastAx[event.jaxis.which][event.jaxis.axis] =
							event.jaxis.value;
					}
					done--;
				}
				else
				{
					if (abs(LastAx[event.jaxis.which][event.jaxis.axis] -
							event.jaxis.value) >= 8192)
					{
						bc->ButtType = BUTTC_JOYSTICK;
						bc->DeviceNum = FindJoystickByInstanceID(event.jaxis.which);
						bc->ButtonNum = (0x8000 | event.jaxis.axis |
										 ((event.jaxis.value < 0)
											  ? 0x4000
											  : 0));
						return (1);
					}
					else
						done--;
				}
				break;
			default:
				done--;
			}
		}
		if (done)
			break;

		// If the button config window is Closed,
		// get out of loop.
		if (buttonConfigStatus != NULL)
		{
			if (*buttonConfigStatus == 0)
			{
				break;
			}
		}
	}

	return (0);
}

extern Config *g_config;

static const char *stdPortInputEncode(int v)
{
	const char *s;

	switch (v)
	{
	default:
	case SI_NONE:
		s = "SI_NONE";
		break;
	case SI_GAMEPAD:
		s = "SI_GAMEPAD";
		break;
	case SI_ZAPPER:
		s = "SI_ZAPPER";
		break;
	case SI_POWERPADA:
		s = "SI_POWERPADA";
		break;
	case SI_POWERPADB:
		s = "SI_POWERPADB";
		break;
	case SI_ARKANOID:
		s = "SI_ARKANOID";
		break;
	}
	return s;
}

static int stdPortInputDecode(const char *s)
{
	int ret = SI_NONE;

	if (s[0] == 0)
	{
		return ret;
	}

	if (isdigit(s[0]))
	{
		ret = atoi(s);
	}
	else
	{
		if (strcmp(s, "SI_GAMEPAD") == 0)
		{
			ret = SI_GAMEPAD;
		}
		else if (strcmp(s, "SI_ZAPPER") == 0)
		{
			ret = SI_ZAPPER;
		}
		else if (strcmp(s, "SI_POWERPADA") == 0)
		{
			ret = SI_POWERPADA;
		}
		else if (strcmp(s, "SI_POWERPADB") == 0)
		{
			ret = SI_POWERPADB;
		}
		else if (strcmp(s, "SI_ARKANOID") == 0)
		{
			ret = SI_ARKANOID;
		}
	}

	return ret;
}

static const char *expPortInputEncode(int v)
{
	const char *s;

	switch (v)
	{
	default:
	case SIFC_NONE:
		s = "SIFC_NONE";
		break;
	case SIFC_ARKANOID:
		s = "SIFC_ARKANOID";
		break;
	case SIFC_SHADOW:
		s = "SIFC_SHADOW";
		break;
	case SIFC_HYPERSHOT:
		s = "SIFC_HYPERSHOT";
		break;
	case SIFC_FKB:
		s = "SIFC_FKB";
		break;
	case SIFC_MAHJONG:
		s = "SIFC_MAHJONG";
		break;
	case SIFC_QUIZKING:
		s = "SIFC_QUIZKING";
		break;
	case SIFC_FTRAINERA:
		s = "SIFC_FTRAINERA";
		break;
	case SIFC_FTRAINERB:
		s = "SIFC_FTRAINERB";
		break;
	case SIFC_OEKAKIDS:
		s = "SIFC_OEKAKIDS";
		break;
	case SIFC_TOPRIDER:
		s = "SIFC_TOPRIDER";
		break;
	}
	return s;
}

static int expPortInputDecode(const char *s)
{
	int ret = SIFC_NONE;

	if (s[0] == 0)
	{
		return ret;
	}

	if (isdigit(s[0]))
	{
		ret = atoi(s);
	}
	else
	{
		if (strcmp(s, "SIFC_ARKANOID") == 0)
		{
			ret = SIFC_ARKANOID;
		}
		else if (strcmp(s, "SIFC_SHADOW") == 0)
		{
			ret = SIFC_SHADOW;
		}
		else if (strcmp(s, "SIFC_HYPERSHOT") == 0)
		{
			ret = SIFC_HYPERSHOT;
		}
		else if (strcmp(s, "SIFC_FKB") == 0)
		{
			ret = SIFC_FKB;
		}
		else if (strcmp(s, "SIFC_MAHJONG") == 0)
		{
			ret = SIFC_MAHJONG;
		}
		else if (strcmp(s, "SIFC_QUIZKING") == 0)
		{
			ret = SIFC_QUIZKING;
		}
		else if (strcmp(s, "SIFC_FTRAINERA") == 0)
		{
			ret = SIFC_FTRAINERA;
		}
		else if (strcmp(s, "SIFC_FTRAINERB") == 0)
		{
			ret = SIFC_FTRAINERB;
		}
		else if (strcmp(s, "SIFC_OEKAKIDS") == 0)
		{
			ret = SIFC_OEKAKIDS;
		}
		else if (strcmp(s, "SIFC_TOPRIDER") == 0)
		{
			ret = SIFC_TOPRIDER;
		}
	}

	return ret;
}

static bool boolDecode(const char *s)
{
	bool ret = false;

	if (isdigit(s[0]))
	{
		ret = atoi(s) != 0;
	}
	else if (strcasecmp(s, "true") == 0)
	{
		ret = true;
	}
	return ret;
}

int saveInputSettingsToFile(const char *filename)
{
	QDir dir;
	std::string path;
	const char *baseDir = FCEUI_GetBaseDirectory();
	char base[512];

	path = std::string(baseDir) + "/input/presets/";

	dir.mkpath(QString::fromStdString(path));

	if (filename != NULL)
	{
		getFileBaseName(filename, base, NULL);

		path += std::string(base) + ".pre";
	}
	else
	{
		const char *romFile = getRomFile();

		if (romFile == NULL)
		{
			return -1;
		}
		getFileBaseName(romFile, base, NULL);

		path += std::string(base) + ".pre";
	}

	FILE *fp = fopen(path.c_str(), "w");

	if (fp == NULL)
	{
		return -1;
	}
	fprintf(fp, "# Input Port Settings\n");
	fprintf(fp, "InputTypePort1   = %s \n", stdPortInputEncode(CurInputType[0]));
	fprintf(fp, "InputTypePort2   = %s \n", stdPortInputEncode(CurInputType[1]));
	fprintf(fp, "InputTypeExpPort = %s \n", expPortInputEncode(CurInputType[2]));
	fprintf(fp, "Enable4Score     = %i \n", (eoptions & EO_FOURSCORE) ? 1 : 0);
	fprintf(fp, "EnableMicPort2   = %i \n", replaceP2StartWithMicrophone);

	fclose(fp);

	return 0;
}

int loadInputSettingsFromFile(const char *filename)
{
	QDir dir;
	std::string path;
	const char *baseDir = FCEUI_GetBaseDirectory();
	char base[512], line[256];
	char id[128], val[128];
	int i, j;

	path = std::string(baseDir) + "/input/presets/";

	dir.mkpath(QString::fromStdString(path));

	if (filename != NULL)
	{
		getFileBaseName(filename, base, NULL);

		path += std::string(base) + ".pre";
	}
	else
	{
		const char *romFile = getRomFile();

		if (romFile == NULL)
		{
			return -1;
		}
		getFileBaseName(romFile, base, NULL);

		path += std::string(base) + ".pre";
	}

	FILE *fp = fopen(path.c_str(), "r");

	if (fp == NULL)
	{
		return -1;
	}

	while (fgets(line, sizeof(line) - 1, fp) != 0)
	{
		i = 0;
		while (line[i] != 0)
		{
			if (line[i] == '#')
			{
				line[i] = 0;
				break;
			}
			i++;
		}

		i = 0;
		while (isspace(line[i]))
			i++;

		j = 0;
		while (isalnum(line[i]) || (line[i] == '_'))
		{
			id[j] = line[i];
			i++;
			j++;
		}
		id[j] = 0;

		if (j == 0)
			continue;

		while (isspace(line[i]))
			i++;

		if (line[i] == '=')
			i++;

		while (isspace(line[i]))
			i++;

		j = 0;
		while (line[i] && !isspace(line[i]))
		{
			val[j] = line[i];
			i++;
			j++;
		}
		val[j] = 0;

		if (j == 0)
		{
			printf("Warning: No Value Specified for Token ID: '%s'\n", id);
			continue;
		}
		//printf("ID: '%s'   Val: '%s' \n", id, val );

		if (strcmp(id, "InputTypePort1") == 0)
		{
			CurInputType[0] = UsrInputType[0] = stdPortInputDecode(val);
		}
		else if (strcmp(id, "InputTypePort2") == 0)
		{
			CurInputType[1] = UsrInputType[1] = stdPortInputDecode(val);
		}
		else if (strcmp(id, "InputTypeExpPort") == 0)
		{
			CurInputType[2] = UsrInputType[2] = expPortInputDecode(val);
		}
		else if (strcmp(id, "Enable4Score") == 0)
		{
			if (boolDecode(val))
			{
				eoptions &= ~EO_FOURSCORE;
			}
			else
			{
				eoptions |= EO_FOURSCORE;
			}
		}
		else if (strcmp(id, "EnableMicPort2") == 0)
		{
			replaceP2StartWithMicrophone = boolDecode(val);
		}
	}

	fclose(fp);

	return 0;
}

/**
 * Hack to map the new configuration onto the existing button
 * configuration management.  Will probably want to change this in the
 * future - soules.
 */
void UpdateInput(Config *config)
{
	char buf[64];
	std::string device, prefix;

	InitJoysticks();

	for (unsigned int i = 0; i < 3; i++)
	{
		snprintf(buf, 64, "SDL.Input.%u", i);
		config->getOption(buf, &device);

		if (device == "None")
		{
			UsrInputType[i] = SI_NONE;
		}
		else if (device.find("GamePad") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_GAMEPAD : (int)SIFC_NONE;
		}
		else if (device.find("PowerPad.0") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_POWERPADA : (int)SIFC_NONE;
		}
		else if (device.find("PowerPad.1") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_POWERPADB : (int)SIFC_NONE;
		}
		else if (device.find("QuizKing") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_NONE : (int)SIFC_QUIZKING;
		}
		else if (device.find("HyperShot") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_NONE : (int)SIFC_HYPERSHOT;
		}
		else if (device.find("Mahjong") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_NONE : (int)SIFC_MAHJONG;
		}
		else if (device.find("TopRider") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_NONE : (int)SIFC_TOPRIDER;
		}
		else if (device.find("FTrainer") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_NONE : (int)SIFC_FTRAINERA;
		}
		else if (device.find("FamilyKeyBoard") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_NONE : (int)SIFC_FKB;
		}
		else if (device.find("OekaKids") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_NONE : (int)SIFC_OEKAKIDS;
		}
		else if (device.find("Arkanoid") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_ARKANOID : (int)SIFC_ARKANOID;
		}
		else if (device.find("Shadow") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_NONE : (int)SIFC_SHADOW;
		}
		else if (device.find("Zapper") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_ZAPPER : (int)SIFC_NONE;
		}
		else if (device.find("BWorld") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_NONE : (int)SIFC_BWORLD;
		}
		else if (device.find("4Player") != std::string::npos)
		{
			UsrInputType[i] = (i < 2) ? (int)SI_NONE : (int)SIFC_4PLAYER;
		}
		else
		{
			// Unknown device
			UsrInputType[i] = SI_NONE;
		}
	}

	// update each of the devices' configuration structure
	// XXX soules - this is temporary until this file is cleaned up to
	//              simplify the interface between configuration and
	//              structure data.  This will likely include the
	//              removal of multiple input buttons for a single
	//              input device key.
	int type, devnum, button;

	// This is now done in InitJoysticks
	// gamepad 0 - 3
	//for (unsigned int i = 0; i < GAMEPAD_NUM_DEVICES; i++)
	//{
	//	char buf[64];
	//	snprintf(buf, sizeof(buf) - 1, "SDL.Input.GamePad.%u.", i);
	//	prefix = buf;

	//	config->getOption(prefix + "DeviceType", &device);
	//	config->getOption(prefix + "DeviceGUID", &guid);
	//	config->getOption(prefix + "Profile", &mapping);

	//	GamePad[i].init(i, guid.c_str(), mapping.c_str());
	//}

	// PowerPad 0 - 1
	for (unsigned int i = 0; i < POWERPAD_NUM_DEVICES; i++)
	{
		char buf[64];
		snprintf(buf, sizeof(buf) - 1, "SDL.Input.PowerPad.%u.", i);
		prefix = buf;

		config->getOption(prefix + "DeviceType", &device);
		if (device.find("Keyboard") != std::string::npos)
		{
			type = BUTTC_KEYBOARD;
		}
		else if (device.find("Joystick") != std::string::npos)
		{
			type = BUTTC_JOYSTICK;
		}
		else
		{
			type = 0;
		}

		config->getOption(prefix + "DeviceNum", &devnum);
		for (unsigned int j = 0; j < POWERPAD_NUM_BUTTONS; j++)
		{
			config->getOption(prefix + PowerPadNames[j], &button);

			powerpadsc[i][j].ButtType = type;
			powerpadsc[i][j].DeviceNum = devnum;
			powerpadsc[i][j].ButtonNum = button;
		}
	}

	// QuizKing
	prefix = "SDL.Input.QuizKing.";
	config->getOption(prefix + "DeviceType", &device);
	if (device.find("Keyboard") != std::string::npos)
	{
		type = BUTTC_KEYBOARD;
	}
	else if (device.find("Joystick") != std::string::npos)
	{
		type = BUTTC_JOYSTICK;
	}
	else
	{
		type = 0;
	}
	config->getOption(prefix + "DeviceNum", &devnum);
	for (unsigned int j = 0; j < QUIZKING_NUM_BUTTONS; j++)
	{
		config->getOption(prefix + QuizKingNames[j], &button);

		QuizKingButtons[j].ButtType = type;
		QuizKingButtons[j].DeviceNum = devnum;
		QuizKingButtons[j].ButtonNum = button;
	}

	// HyperShot
	prefix = "SDL.Input.HyperShot.";
	config->getOption(prefix + "DeviceType", &device);
	if (device.find("Keyboard") != std::string::npos)
	{
		type = BUTTC_KEYBOARD;
	}
	else if (device.find("Joystick") != std::string::npos)
	{
		type = BUTTC_JOYSTICK;
	}
	else
	{
		type = 0;
	}
	config->getOption(prefix + "DeviceNum", &devnum);
	for (unsigned int j = 0; j < HYPERSHOT_NUM_BUTTONS; j++)
	{
		config->getOption(prefix + HyperShotNames[j], &button);

		HyperShotButtons[j].ButtType = type;
		HyperShotButtons[j].DeviceNum = devnum;
		HyperShotButtons[j].ButtonNum = button;
	}

	// Mahjong
	prefix = "SDL.Input.Mahjong.";
	config->getOption(prefix + "DeviceType", &device);
	if (device.find("Keyboard") != std::string::npos)
	{
		type = BUTTC_KEYBOARD;
	}
	else if (device.find("Joystick") != std::string::npos)
	{
		type = BUTTC_JOYSTICK;
	}
	else
	{
		type = 0;
	}
	config->getOption(prefix + "DeviceNum", &devnum);
	for (unsigned int j = 0; j < MAHJONG_NUM_BUTTONS; j++)
	{
		config->getOption(prefix + MahjongNames[j], &button);

		MahjongButtons[j].ButtType = type;
		MahjongButtons[j].DeviceNum = devnum;
		MahjongButtons[j].ButtonNum = button;
	}

	// TopRider
	prefix = "SDL.Input.TopRider.";
	config->getOption(prefix + "DeviceType", &device);
	if (device.find("Keyboard") != std::string::npos)
	{
		type = BUTTC_KEYBOARD;
	}
	else if (device.find("Joystick") != std::string::npos)
	{
		type = BUTTC_JOYSTICK;
	}
	else
	{
		type = 0;
	}
	config->getOption(prefix + "DeviceNum", &devnum);
	for (unsigned int j = 0; j < TOPRIDER_NUM_BUTTONS; j++)
	{
		config->getOption(prefix + TopRiderNames[j], &button);

		TopRiderButtons[j].ButtType = type;
		TopRiderButtons[j].DeviceNum = devnum;
		TopRiderButtons[j].ButtonNum = button;
	}

	// FTrainer
	prefix = "SDL.Input.FTrainer.";
	config->getOption(prefix + "DeviceType", &device);
	if (device.find("Keyboard") != std::string::npos)
	{
		type = BUTTC_KEYBOARD;
	}
	else if (device.find("Joystick") != std::string::npos)
	{
		type = BUTTC_JOYSTICK;
	}
	else
	{
		type = 0;
	}
	config->getOption(prefix + "DeviceNum", &devnum);
	for (unsigned int j = 0; j < FTRAINER_NUM_BUTTONS; j++)
	{
		config->getOption(prefix + FTrainerNames[j], &button);

		FTrainerButtons[j].ButtType = type;
		FTrainerButtons[j].DeviceNum = devnum;
		FTrainerButtons[j].ButtonNum = button;
	}

	// FamilyKeyBoard
	prefix = "SDL.Input.FamilyKeyBoard.";
	config->getOption(prefix + "DeviceType", &device);
	if (device.find("Keyboard") != std::string::npos)
	{
		type = BUTTC_KEYBOARD;
	}
	else if (device.find("Joystick") != std::string::npos)
	{
		type = BUTTC_JOYSTICK;
	}
	else
	{
		type = 0;
	}
	config->getOption(prefix + "DeviceNum", &devnum);
	for (unsigned int j = 0; j < FAMILYKEYBOARD_NUM_BUTTONS; j++)
	{
		config->getOption(prefix + FamilyKeyBoardNames[j], &button);

		fkbmap[j].ButtType = type;
		fkbmap[j].DeviceNum = devnum;
		fkbmap[j].ButtonNum = button;
	}
}

// Definitions from main.h:
// GamePad defaults
const char *GamePadNames[GAMEPAD_NUM_BUTTONS] = {"A", "B", "Select", "Start",
						 "Up", "Down", "Left", "Right", "TurboA", "TurboB"};
const char *DefaultGamePadDevice[GAMEPAD_NUM_DEVICES] =
	{"Keyboard", "None", "None", "None"};
const int DefaultGamePad[GAMEPAD_NUM_DEVICES][GAMEPAD_NUM_BUTTONS] =
	{{SDLK_f, SDLK_d, SDLK_s, SDLK_RETURN,
	  SDLK_UP, SDLK_DOWN, SDLK_LEFT, SDLK_RIGHT, -1, -1},
	 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
	 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
	 {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};

// PowerPad defaults
const char *PowerPadNames[POWERPAD_NUM_BUTTONS] =
	{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B"};
const char *DefaultPowerPadDevice[POWERPAD_NUM_DEVICES] =
	{"Keyboard", "None"};
const int DefaultPowerPad[POWERPAD_NUM_DEVICES][POWERPAD_NUM_BUTTONS] =
	{{SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET,
	  SDLK_k, SDLK_l, SDLK_SEMICOLON, SDLK_QUOTE,
	  SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH},
	 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};

// QuizKing defaults
const char *QuizKingNames[QUIZKING_NUM_BUTTONS] =
	{"0", "1", "2", "3", "4", "5"};
const char *DefaultQuizKingDevice = "Keyboard";
const int DefaultQuizKing[QUIZKING_NUM_BUTTONS] =
	{SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y};

// HyperShot defaults
const char *HyperShotNames[HYPERSHOT_NUM_BUTTONS] = {"0", "1", "2", "3"};

const char *DefaultHyperShotDevice = "Keyboard";
const int DefaultHyperShot[HYPERSHOT_NUM_BUTTONS] =
	{SDLK_q, SDLK_w, SDLK_e, SDLK_r};

// Mahjong defaults
const char *MahjongNames[MAHJONG_NUM_BUTTONS] =
	{"00", "01", "02", "03", "04", "05", "06", "07",
	 "08", "09", "10", "11", "12", "13", "14", "15",
	 "16", "17", "18", "19", "20"};

const char *DefaultMahjongDevice = "Keyboard";
const int DefaultMahjong[MAHJONG_NUM_BUTTONS] =
	{SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_a, SDLK_s, SDLK_d,
	 SDLK_f, SDLK_g, SDLK_h, SDLK_j, SDLK_k, SDLK_l, SDLK_z, SDLK_x,
	 SDLK_c, SDLK_v, SDLK_b, SDLK_n, SDLK_m};

// TopRider defaults
const char *TopRiderNames[TOPRIDER_NUM_BUTTONS] =
	{"0", "1", "2", "3", "4", "5", "6", "7"};
const char *DefaultTopRiderDevice = "Keyboard";
const int DefaultTopRider[TOPRIDER_NUM_BUTTONS] =
	{SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y, SDLK_u, SDLK_i};

// FTrainer defaults
const char *FTrainerNames[FTRAINER_NUM_BUTTONS] =
	{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B"};
const char *DefaultFTrainerDevice = "Keyboard";
const int DefaultFTrainer[FTRAINER_NUM_BUTTONS] =
	{SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET,
	 SDLK_k, SDLK_l, SDLK_SEMICOLON, SDLK_QUOTE,
	 SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH};

// FamilyKeyBoard defaults
const char *FamilyKeyBoardNames[FAMILYKEYBOARD_NUM_BUTTONS] =
	{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8",
	 "1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
	 "MINUS", "EQUAL", "BACKSLASH", "BACKSPACE",
	 "ESCAPE", "Q", "W", "E", "R", "T", "Y", "U", "I", "O",
	 "P", "GRAVE", "BRACKET_LEFT", "ENTER",
	 "LEFTCONTROL", "A", "S", "D", "F", "G", "H", "J", "K",
	 "L", "SEMICOLON", "APOSTROPHE", "BRACKET_RIGHT", "INSERT",
	 "LEFTSHIFT", "Z", "X", "C", "V", "B", "N", "M", "COMMA",
	 "PERIOD", "SLASH", "RIGHTALT", "RIGHTSHIFT", "LEFTALT", "SPACE",
	 "DELETE", "END", "PAGEDOWN",
	 "CURSORUP", "CURSORLEFT", "CURSORRIGHT", "CURSORDOWN"};

const char *DefaultFamilyKeyBoardDevice = "Keyboard";
const int DefaultFamilyKeyBoard[FAMILYKEYBOARD_NUM_BUTTONS] =
	{SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, SDLK_F7, SDLK_F8,
	 SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5,
	 SDLK_6, SDLK_7, SDLK_8, SDLK_9, SDLK_0,
	 SDLK_MINUS, SDLK_EQUALS, SDLK_BACKSLASH, SDLK_BACKSPACE,
	 SDLK_ESCAPE, SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y, SDLK_u,
	 SDLK_i, SDLK_o, SDLK_p, SDLK_BACKQUOTE, SDLK_LEFTBRACKET, SDLK_RETURN,
	 SDLK_LCTRL, SDLK_a, SDLK_s, SDLK_d, SDLK_f, SDLK_g, SDLK_h, SDLK_j,
	 SDLK_k, SDLK_l, SDLK_SEMICOLON, SDLK_QUOTE, SDLK_RIGHTBRACKET,
	 SDLK_INSERT, SDLK_LSHIFT, SDLK_z, SDLK_x, SDLK_c, SDLK_v, SDLK_b,
	 SDLK_n, SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH, SDLK_RALT,
	 SDLK_RSHIFT, SDLK_LALT, SDLK_SPACE, SDLK_DELETE, SDLK_END, SDLK_PAGEDOWN,
	 SDLK_UP, SDLK_LEFT, SDLK_RIGHT, SDLK_DOWN};
