////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 RWS Inc, All Rights Reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of version 2 of the GNU General Public License as published by
// the Free Software Foundation
//
// 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
//
// menus.cpp
// Project: Postal
//
// This module contains the actual menu data (structs, strings, etc.)
//
// History:
//		12/05/96 MJR	Started.
//
//		12/19/96	JMI	Menus contained a pointer to guiItem which was a not-yet-
//							created gui from my old menu sample code.  Removed ptr.
//							Typo in name of MainMenuTile.bmp.
//
//		12/19/96	JMI	Offset menu items so they didn't overwrite menu heading
//							and moved "Ready?" from verify exit menu to the heading.
//
//		01/27/97	JMI	Added more menus (High Scores, Options, and Start).
//
//		01/28/97	JMI	Added new menu background and indicator for two menus
//							(Main and VerifyExit).
//
//		01/28/97	JMI	Was setting menu box to not transparent instead of trans-
//							parent.  Fixed.
//
//		01/29/97	JMI	Now uses SampleMaster to play a sample when a choice is
//							made from the main menu.
//
//		01/30/97	JMI	Added cheezy brighten/darken effect via scrollbar in
//							options menu.
//
//		01/31/97	JMI	Made all menus uppercase.  Also, uses flesh menu and
//							smash font for all menus now.  Adjusted menu paths
//							so they are in the res/Menus dir and font paths so
//							they are in the res/Fonts dir.
//
//		01/31/97	JMI	Adjusted menu sizes such that the flesh menu could be
//							shown.
//
//		02/02/97	JMI	Added support for -1 passed on MenuChoice callback
//							indicating change in focused menu item.  Added use of
//							new child struct in Menu struct, MenuGui.  Relocated
//							title text on some menus.  Resized font on some menus
//							to fit better.  Got rid of PalTran func which was doing
//							nothing now that game.cpp does CLUT red shift/fade.
//
//		02/03/97	JMI	Now menuClientGame invokes the options menu, if selected.
//							Adjusted paths for SAK dir.
//
//		02/04/97	JMI	Now uses functions in game.cpp to get and set the gamma
//							level.
//
//		02/04/97	JMI	Forgot to release memfile used to get scrollbar for options
//							menu.
//
//		02/10/97	JMI	rspReleaseResource() now takes a ptr to a ptr.
//
//		02/19/97	JMI	More implementation for Start Game menu.
//
//		03/06/97	JMI	Added g_menuVerifyQuitGame.
//
//		03/07/97	JMI	Added Load menu option on main menu.
//
//		03/28/97	JMI	Disabled help and ordering info.
//
//		04/07/97	JMI	When I added the Load menu option, I must've screwed up
//							the StartGameMenu() choice function so that it always
//							started a game.  Fixed.
//
//		04/07/97	JMI	Now uses g_GameSettings.m_szServerName for the server name.
//
//		04/08/97	JMI	Added multiplayer options menu (menuMultiOptions).
//
//		04/11/97	JMI	Added record and play demo options to Start Game Menu.
//
//		04/14/97	JMI	Swapped positions of Record and Play menu options.
//
//		04/21/97	JMI	Added menuEditor and utilized new auto go back
//							functionality in Menu API.
//
//		04/21/97	JMI	menuEditor now specifies the palette indices to be set by
//							the Menu API.
//
//		04/22/97	JMI	Added controls menu and sub menus for keyboard, mouse, and
//							joystick.  Only the mouse sub menu is partially implemented
//							functionally (the rest are not yet implemented).
//
//		04/22/97	JMI	Added checkbox for enabling mouse input.
//
//		04/22/97	JMI	Separated game start options into 3 categories, single
//							player, multiplayer, and demo.
//
//		04/22/97	JMI	Updated menuClient to work as the in game menu for Play.cpp.
//
//		04/22/97	JMI	No longer uses chdir() to get the open dialog into the
//							correct directory.  Now we simply default szRealmName
//							to the path of the dir we want to save under.
//
//		04/22/97	JMI	Now VerifyQuitMenuChoice handles menuClientGame instead of
//							g_menuVerifyQuit.
//
//		04/24/97	JMI	Took out the ! in "Postal!" title of main menu.
//
//		04/24/97	JMI	Now uses the centering flag for header text and
//							two new flags for shadowing the header and the items.
//							Now colors are specified as RGBA and background font
//							color must be transparent (not offered as an option).
//
//		04/24/97	JMI	Change menuClientGame's "GAME MENU" to "MENU".
//
//		04/25/97	JMI	Changed menu item text shadow color to be more blood like.
//							Unified all menu header and item text sizes.
//							Scooted menu items in a bit to avoid having the bullet in-
//							dicator go off the menu.
//
//		04/25/97	JMI	StartSingleMenu() was not calling Play_SetRealmName(NULL)
//							to clear the realm name so the game went in level order.
//
//		05/06/97	JMI	Now the Gamma GUI displays text next to the scrollbar.
//
//		05/07/97	JMI	Now the range of the Gamma GUI text is -50 to +50.
//
//		05/15/97	JMI	Added the menu for disabling features like alpha blending
//							and X-ray effect.
//
//		05/22/97	JMI	Added 3D lighting to the features that can be disabled.
//
//		05/23/97	JMI	Added particle effects to the features that can be disabled.
//
//		05/25/97	JMI	Swapped positions of Join and Host on Multiplayer menu.
//
//		05/29/97	JMI	Now, when g_GameSettings.m_sServer is set,
//							g_GameSettings.m_sClient is set as well.
//
//		06/11/97	JMI	Added g_MenuSettings.
//
//		06/12/97 MJR	Moved much of the callback code from here into game.cpp.
//
//		07/01/97	JMI	Now plays a different sound for a selected item vs. an
//							item selection change.
//
//		07/03/97	JMI	Now calls Game_ControlsMenu() with choices from
//							menuControls.
//
//		07/04/97	JMI	Added the appropriate amount of items for the keyboard,
//							mouse, and joystick menus.
//							Also, moved 'Use Mouse' to Options menu.
//							Also, changed "CONTROLS" option on Options menu to "EDIT
//							CONTROLS", "KEYBOARD" to "EDIT KEYS", and collapsed
//							"MOUSE" & "JOYSTICK" into "EDIT BUTTONS".
//
//		07/07/97	JMI	Added callbacks for menuJoystick.
//
//		07/12/97	JMI	Removed menuHighScores and its reference by menuMain.
//							Also, adjusted MenuChoice switch values to compensate for
//							choices after 'High Scores' being pushed back (3..5
//							became 2..4).
//
//		07/13/97	JMI	Added Challenge option from menuStart that leads to new
//							menuChallenge menu.
//
//		07/14/97	JMI	Added difficulty slider to menuOptions.
//
//		07/16/97	JMI	Added menuVolumes and path from menuOptions.
//
//		07/18/97	JMI	Got rid of bogus immitation PlaySample functions.
//							Now there is one PlaySample() function.  Also, you now
//							MUST specify a category and you don't have to specify a
//							SoundInstance ptr to specify a volume.
//							Also, added an '_' in "START GAME".
//
//		07/18/97	JMI	Changed "LOAD" to "LOAD_LEVEL" and added some underscores
//							for spaces in "LOAD GAME" and "THE GAUNTLET".
//
//		07/19/97	JMI	Added 'Volume Distancing' to menuFeatures.
//							Now 'Options' launches many other menus for the options:
//							menuVideo, menuAudio, menuPlayOptions, menuInput.
//
//		07/21/97	JMI	Changed "FEATURES" menu to "PERFORMANCE".
//
//		07/29/97	JMI	Now uses in-thumb area for text representation of current
//							gamma value.  The gamma range can be adjusted from the
//							scrollbar file like before, but now, the range in the
//							scrollbar will be mapped to the range GAMMA_RANGE so the
//							user's control can be more or less granular than the actual
//							gamma range.
//
//		08/01/97	JMI	Now there's a 'SETUP' option on the menuStartMulti that
//							goes to menuMultiOptions.
//							Also, changed 'Other' in multi options menu to net port.
//
//		08/03/97 BRH	Added network protocol option to the multiplayer setup
//							menu.
//
//		08/04/97	JMI	Added sliders for mouse sensitivities (X & Y).
//							Added menu with sliders for all facets of rotation.
//
//		08/04/97	JMI	Added performance option for playing ambient sounds.
//
//		08/05/97	JMI	Added multiplayer option for connection type.
//
//		08/05/97	JMI	Added 'Touch Organ' option to menuAudioOptions.
//
//		08/06/97	JMI	Changed "MOUSE SENS. X" and "... Y" to better names and
//							use Columnize Guis flag on menuControls.
//
//		08/07/97 BRH	Fixed problem where the port numnber was not getting
//							set from the gui.
//
//		08/12/97	JMI	Added some '...'s to try that concept with menus where you
//							want to know.
//
//		08/13/97 MJR	Added Host Name gui.
//
//		08/14/97	JMI	Converted menu callbacks to returning true to accept or
//							false to deny.
//
//		08/15/97	JRD	Added separate postal organ menu,
//
//		08/17/97 BRH	Got rid of violence option on PlayOptions menu, and
//							got rid of help on the main menu.
//
//		08/17/97	JMI	Now releases protocol chooser GUI.
//
//		08/17/97	JMI	menuOrgan was calling AudioOptionsInit() and
//							AudioOptionsChoice() so choices on the organ menu were
//							affecting audio options.  Changed to no init and
//							Organ_MenuChoice() as the choice callback.
//							Also, added two lines explaining briefly that you can hit
//							A..Z and 0..9 to play with the organ.
//
//		08/17/97	JMI	Changed wording on menuOrgan.
//
//		08/17/97	MJR	Now loads from g_resmgrShell, and uses new menu bg's.
//
//		08/18/97 BRH	Changed protocol gui from an REdit to an RTxt gui.
//
//		08/20/97 BRH	Added new samples for the new pain and suffering volume
//							adjustment categories.
//
//					MJR	Changed multiplayer-related menus and added a new one.
//
//		08/23/97	JMI	Now the init/kill callback for the main menu calls game.cpp
//							to hook it in.
//
//		08/23/97	JMI	Added better player color menu feedback.
//
//		08/23/97	JMI	Moved ms_apszPlayerColorDescriptions[] from here into
//							CGameSettings where it can be used more generally.
//
//		08/24/97 MJR	Lots of tuning for consistancy, wording, etc.
//							More and more tuning.
//							Removed grip-zone-radius stuff -- no longer needed.
//
//		08/25/97 MJR	Made the color stuff into macros so they could easily be
//							changed for all menus.  Changed the RGB values, but the
//							color mapping didn't seem to be affected!!!
//
//		08/25/97	JMI	Colors were mapping to windows static colors so they did
//							not apparently change.  Added mappable range to all menus
//							and made it so all menus set the palette as well.
//							Also, added code for new difficulty GUI which provides a
//							slider and a text area.
//							Also, changed the unspecified sound category sampler to
//							the mine beep.
//							Also, now scales the ranges of the sound sliders to between
//							0 to 10 for the user and sets the value scaled to the selected
//							category's adjusted value for the current quality.
//
//		08/26/97	JMI	Changed difficulty strings and name of PLAY menu to
//							DIFFICULTY.  Also, changed the DIFFICULTY item to something
//							shorter like SET.
//
//		08/27/97	JMI	Now when you choose 'Defaults' from the Mixer menu, it
//							plays all the samples at the default volume for feedback
//							(rather than just the last one).
//
//		08/27/97	JMI	Took all underscores out of menu titles and items now that
//							RPrefs supports spaces in vars and sections.
//
//		08/27/97	JMI	Changed "PLAY" option on Options menu to "DIFFICULTY".
//
//		08/27/97	JMI	Now displays rotation degree values as integrals.
//							Also, all menus no use g_fontPostal for their helper GUIs.
//
//		08/27/97	JMI	Removed "BACK" menu items.  Actually replaced with a
//							disabled "".  Note that this is not required by the menu
//							system.  That is, you can set the cancel item to an item
//							that doesn't exist.  The problem is there's no mechanism for
//							specifying the one after the last item and so each menu would
//							have to constantly be updated.
//							Also, now enter on any item in the mixer will play all the
//							sounds simultaneously so you can get an idea of their
//							combined levels.
//							Also, changed sampler for Unspecified to the empty weapon
//							noise (was mine beep).
//
//		09/02/97	JMI	Added browse option to menuStartDemo.
//
//		09/04/97	JMI	Removed browse option from menuStartDemo.
//
//		09/06/97	JMI	No longer displays the Demo menu even for an instant if
//							g_GameSettings.m_sCanRecordDemos is FALSE.
//
//		09/17/97	JMI	Menus now use localizable string variables from localize.*.
//
//		09/26/97	JMI	Now uses the 'Restore defaults' string from localize.
//
//		09/26/97	JMI	Resized menus to fit localization text for French & German:
//
//							Menu							Change		Language that caused
//							======================	==========	=======================
//							g_menuVerifyQuitGame		SM -> MD		French, German
//							menuVerifyExit				SM -> MD		French, German
//							menuOptions					SM -> MD		French, German
//							menuRotation				MD -> LG		French
//							menuStart					SM -> MD		French, German
//							menuStartSingle			SM -> MD		French, German
//							menuStartMulti				SM -> MD		French, German
//							menuChallenge 				SM -> MD		German
//							menuJoinMulti				MD -> LG		French, German
//
//		10/10/97	JMI	Put menuJoystick back on menuControls based on conditional
//							compile option ALLOW_JOYSTICK.  Also, updated
//							ControlsChoice() amd ControlsInit() to handle these cases.
//
//		10/13/97	JMI	Moved g_pszControlsMenu_UseJoystick to localize.* and took
//							out 'TEMP' comments.
//							Also, made 'Controls' menu a large menu b/c the new
//							joystick options were pushing things off the edge.
//
//		12/01/97 BRH	Added #ifdef SPAWN sections to menus so that that spawn
//							verison of the game has different menus that only allow
//							them to join a multiplayer game.
//
//		12/04/97 BRH	Added additional option for single player missions so they
//							can play the original postal levels or the add on pack
//							levels.
//
//		10/07/99	JMI	Conditional remove Add On start menu option when in
//							SUPER_POSTAL target.
//
//		03/30/00 MJR	Switched to START_MENU_ADDON_ITEM to control whether
//							the START menu has an Add-On item.
//
////////////////////////////////////////////////////////////////////////////////
#define MENUS_CPP

#include "RSPiX.h"
#ifdef PATHS_IN_INCLUDES
#include "WishPiX/Menu/menu.h"
#else
#include "menu.h"
#endif
#include "menus.h"
#include "game.h"
#include "play.h"
#include "SampleMaster.h"
#include "input.h"
#include "input.h"
#include "MenuSettings.h"
#include "organ.h"
#include "CompileOptions.h" // For ALLOW_JOYSTICK macro.
#include "update.h"
#include "pvr.h"

#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif

////////////////////////////////////////////////////////////////////////////////
// Macros/types/etc.
////////////////////////////////////////////////////////////////////////////////

#define MENU_BG_MD "Menu/menu_md.bmp"

#define MENU_RECT_SM 20, 20, 280, 350
#define MENU_HEAD_Y_SM -149
#define MENU_ITEM_X_SM 70
#define MENU_ITEM_Y_SM -101
#define MENU_ITEM_SPACE_Y_SM 4
#define MENU_ITEM_IND_SPACE_X_SM 5

#define MENU_RECT_MD 20, 20, 450, 460
#define MENU_HEAD_Y_MD -196
#define MENU_ITEM_X_MD 60
#define MENU_ITEM_Y_MD -140
#define MENU_ITEM_SPACE_Y_MD 4
#define MENU_ITEM_IND_SPACE_X_MD 5

#define MENU_RECT_LG 0, 0, 640, 480
#define MENU_HEAD_Y_LG -195
#define MENU_ITEM_X_LG 60
#define MENU_ITEM_Y_LG -95
#define MENU_ITEM_SPACE_Y_LG 4
#define MENU_ITEM_IND_SPACE_X_LG 5

#define MENU_INDICATOR "Menu/bullet.bmp"

#define SMASH_FONT "Fonts/smash.fnt"
#define HEAD_FONT_HEIGHT 36
#define ITEM_FONT_HEIGHT 28
#define ITEM_FONT_HEIGHT_SMALLER 24

// This is used when setting up font's for GUI's.  The size doesn't matter, but we do have to
// supply a value, so just for kicks, let's use the same one each time.  Since we use RFont to
// try to figure out how many font sizes are used, we want this to be a cached size.
#define DEFAULT_GUI_FONT_HEIGHT 19

#define HEAD_COLOR MAKE_U32_COLOR(160, 0, 0, 0)
#define HEAD_SHADOW_COLOR MAKE_U32_COLOR(64, 16, 16, 0)
#define ITEM_COLOR MAKE_U32_COLOR(160, 0, 0, 0)
#define ITEM_SHADOW_COLOR MAKE_U32_COLOR(64, 16, 16, 0)

// Background color index for where there is no menu graphic.
#define MENU_BG_COLOR 252

// Starting palette entry to set.
#define PAL_SET_START 0
// Number of palette entries to set.
#define PAL_SET_NUM 230

// Starting palette entry we can map menu colors to.
#define PAL_MAP_START 10
// Number of palette entries we can map menu colors to.
#define PAL_MAP_NUM 236

#define GUI_GAMMA_FILE "Menu/GammaSB.gui"
#define GUI_CONNECT_IP_FILE "Menu/JoinIP.gui"
#define GUI_HOST_NAME_FILE "Menu/HostName.gui"
#define PLAYER_NAME_GUI_FILE "Menu/PlayerName.gui"
#define PLAYER_COLOR_GUI_FILE "Menu/PlayerColor.gui"
#define GUI_CHECKBOX_FILE "Menu/CheckBox0.gui"
#define GUI_DIFFICULTY_FILE "Menu/Difficulty.gui"
#define GUI_VOLUME_FILE "Menu/VolumeSB.gui"
#define NET_PORT_GUI_FILE "Menu/NetPort.gui"
#define NET_PROTO_GUI_FILE "Menu/NetProto.gui"
#define NET_CONNECTION_GUI_FILE "Menu/NetConnectionTxt.gui"
#define GUI_MOUSE_SENSITIVITY_FILE "Menu/MouseSensitivitySB.gui"
#define GUI_ROTATION_FILE "Menu/RotationSB.gui"
#define GUI_TAP_ROTATION_FILE "Menu/TapRotationSB.gui"

#define GUI_ID_GRIPRADIUS_VAL 10
#define GUI_ID_GAMMA_VAL 10
#define GUI_ID_SENSITIVITY_VAL 10
#define GUI_ID_ROTATION_VAL 10
#define GUI_ID_VOLUME_VAL 10

#define GUI_ID_DIFFICULTY_SLIDER 8000
#define GUI_ID_DIFFICULTY_TEXT 8001
#define GUI_ID_DIFFICULTY_VAL 8002

#define MIN_GAMMA_VAL 50
#define MAX_GAMMA_VAL 205
#define GAMMA_RANGE (MAX_GAMMA_VAL - MIN_GAMMA_VAL)

#define USER_VOLUME_RANGE 11

#define MOUSE_SENSITIVITY_DIVISOR 5.0

// Determines the number of elements in the passed array at compile time.
#define NUM_ELEMENTS(a) (sizeof(a) / sizeof(a[0]))

extern PVR::cRenderMgr *g_pvrRenderer;
extern int16_t Credits(SampleMasterID* pMusic = NULL, char*	pszBackground = NULL, char* pszCredits = NULL);
extern int16_t Game_LoadPlayersSaveSlot(int8_t cSlotNumber);

////////////////////////////////////////////////////////////////////////////////
// Function prototypes
////////////////////////////////////////////////////////////////////////////////

static void MenuResize(Menu *pMenu, int8_t cSize)
{
	switch (cSize)
	{
		case 0:
			pMenu->menupos.sX = 20;
			pMenu->menupos.sY = 20;
			pMenu->menupos.sW = 450;
			pMenu->menupos.sH = 460;
			pMenu->menupos.sHeaderX = -60;
			pMenu->menupos.sHeaderY = MENU_HEAD_Y_MD;
			pMenu->menupos.sItemX = MENU_ITEM_X_MD;
			pMenu->menupos.sItemY = MENU_ITEM_Y_MD;
			pMenu->menupos.sItemSpacingY = MENU_ITEM_SPACE_Y_MD;
			pMenu->menupos.sIndicatorSpacingX = MENU_ITEM_IND_SPACE_X_MD;
		break;
		case 1:
			pMenu->menupos.sX = 0;
			pMenu->menupos.sY = 0;
			pMenu->menupos.sW = 640;
			pMenu->menupos.sH = 480;
			pMenu->menupos.sHeaderX = -60;
			pMenu->menupos.sHeaderY = MENU_HEAD_Y_LG;
			pMenu->menupos.sItemX = MENU_ITEM_X_LG;
			pMenu->menupos.sItemY = MENU_ITEM_Y_LG;
			pMenu->menupos.sItemSpacingY = MENU_ITEM_SPACE_Y_LG;
			pMenu->menupos.sIndicatorSpacingX = MENU_ITEM_IND_SPACE_X_LG;
			
			/*pMenu->menupos.sX = 20;
			pMenu->menupos.sY = 20;
			pMenu->menupos.sW = 500;
			pMenu->menupos.sH = 460;
			pMenu->menupos.sHeaderX = -60;

			pMenu->menupos.sItemX = MENU_ITEM_X_MD;
			pMenu->menupos.sItemY = MENU_ITEM_Y_MD;
			pMenu->menupos.sItemSpacingY = MENU_ITEM_SPACE_Y_MD;
			pMenu->menupos.sIndicatorSpacingX = MENU_ITEM_IND_SPACE_X_MD;*/
		break;
	}	
}

// todo:
static void ControlOptionsAssisted(RGuiImage *pgi, bool bAlt = false)
{
	pgi->heightPvr = 220;
	pgi->widthPvr = 220;
	pgi->startUPvr = 0.f;
	pgi->startVPvr = 0.f;		
	pgi->endUPvr = 0.531f;
	pgi->endVPvr = 0.548f;

	RPrint print;			
	print.SetDestGuiTextString(pgi->m_pGuiTextString);
	print.SetFont(19, &g_fontBig);		
	print.SetColorRGB(255, 0, 0);			
	print.SetShadowColorRGB(128, 0, 0);	
	print.SetEffectAbs(RPrint::SHADOW_X, 1);
	print.SetEffectAbs(RPrint::SHADOW_Y, 1);
	print.SetWordWrap(TRUE);

	int16_t sX;
	int16_t sY;
	int16_t sX2;
	int16_t sY2;
	int16_t sX3;
	int16_t sY3;

	// Duck	
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:
			pgi->offsetPvrX = 60;
			sX = 125;
			sY = 105;
			
			if (bAlt)
			{
				sX2 = 125;
				sY2 = 125;
			}
			else
			{
				sX2 = 148;
				sY2 = 105;
			}
			
		break;
		case 1:
			pgi->offsetPvrX = 40;
			sX = 155;
			sY = 95;
			sX2 = 155;
			sY2 = 115;
		break;
		case 2:
			pgi->offsetPvrX = 90;
			sX = 125;
			sY = 105;
			sX2 = 125;
			sY2 = 125;
		break;
		case 3:
			pgi->offsetPvrX = 107;
			sX = 145;
			sY = 95;
			sX2 = 145;
			sY2 = 115;
		break;
		case 4:
			pgi->offsetPvrX = 50;			
			if (bAlt)
			{
				sX = 155;
				sY = 95;
				sX2 = 155;
				sY2 = 130;
			}
			else
			{
				sX = 155;
				sY = 95;
				sX2 = 155;
				sY2 = 115;
			}
		break;

		case 5:
			pgi->offsetPvrX = 95;
			sX = 145;
			sY = 100;
			sX2 = 135;
			sY2 = 120;
		break;
	}
	print.SetColumn(sX, sY, 170, 200);

	if (bAlt)
	{
		print.printPVR(g_pszInputLockOn[g_GameSettings.m_cActiveLanguage]);					
	}
	else
	{
		print.printPVR(g_pszInputDuck[g_GameSettings.m_cActiveLanguage]);	
	}
		
	print.SetColumn(sX2, sY2, 170, 200);
	print.printPVR(g_pszInputHold[g_GameSettings.m_cActiveLanguage]);	

	// Fire	
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 229;
			sY = 105;
			sX2 = 257;
			sY2 = 105;
		break;
		case 1:			
			sX = 259;
			sY = 95;
			sX2 = 259;
			sY2 = 115;
		break;
		case 2:			
			sX = 229;
			sY = 105;
			sX2 = 257;
			sY2 = 105;
		break;
		case 3:			
			sX = 249;
			sY = 95;
			sX2 = 249;
			sY2 = 115;
		break;
		case 4:
			sX = 254;
			sY = 95;
			sX2 = 254;
			sY2 = 115;
		break;
		case 5:
			sX = 245;
			sY = 100;
			sX2 = 245;
			sY2 = 120;
		break;
	}
	print.SetColumn(sX, sY, 190, 100);		
	print.printPVR(g_pszInputFire[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX2, sY2, 170, 100);
	print.printPVR(g_pszInputHold[g_GameSettings.m_cActiveLanguage]);	


	// Execute
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 240;
			sY = 142;
			sX2 = 278;
			sY2 = 142;
		break;
		case 1:			
			sX = 215;
			sY = 137;
			sX2 = 265;
			sY2 = 137;
		break;
		case 2:			
			sX = 215;
			sY = 142;
			sX2 = 256;
			sY2 = 142;
		break;
		case 3:			
			sX = 230-5;
			sY = 142;
			sX2 = 271-5;
			sY2 = 142;
		break;
		case 4:
			sX = 205;
			sY = 137;
			sX2 = 251;
			sY2 = 137;
		break;
		case 5:
			sX = 235;
			sY = 137;
			sX2 = 275;
			sY2 = 137;
		break;
	}
	print.SetColumn(sX, sY, 250, 100);			
	print.printPVR(g_pszInputExecute[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX2, sY2, 250, 100);
	print.printPVR("/");

	// Revive
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 240;
			sY = 158;
			sX2 = 270;
			sY2 = 158;
			sX3 = 296;
			sY3 = 158;
		break;
		case 1:			
			sX = 215;
			sY = 157;
			sX2 = 280;
			sY2 = 157;
			sX3 = 310;
			sY3 = 157;
		break;
		case 2:			
			sX = 260;
			sY = 142;
			sX2 = 240;
			sY2 = 158;
			sX3 = 293;
			sY3 = 158;
		break;
		case 3:			
			sX = 271;
			sY = 142;
			sX2 = 246;
			sY2 = 158;
			sX3 = 293+11;
			sY3 = 158;
		break;
		
		case 4:
			sX = 255;
			sY = 137;
			sX2 = 230;
			sY2 = 155;
			sX3 = 256;
			sY3 = 155;			
		break;


		case 5:
			sX = 235;
			sY = 157;
			sX2 = 280;
			sY2 = 157;
			sX3 = 307;
			sY3 = 157;
		break;
	}
	print.SetColumn(sX, sY, 250, 100);
	print.printPVR(g_pszInputRevive[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX2, sY2, 250, 100);
	print.printPVR("(");
	print.SetColumn(sX2 + 3, sY2, 250, 100);
	print.printPVR(g_pszInputCoop[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX3, sY3, 250, 100);
	print.printPVR(")");	

	// Aim on spot
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 249;
			sY = 184;
			sX2 = 249;
			sY2 = 200;			
		break;
		case 1:			
			sX = 270;
			sY = 184;
			sX2 = 270;
			sY2 = 220;			
		break;
		case 2:			
			sX = 246;
			sY = 184;
			sX2 = 246;
			sY2 = 200;			
		break;
		case 3:			
			sX = 246+15;
			sY = 178;
			sX2 = 246+15;
			sY2 = 214;
		break;
		case 4:
			sX = 272;
			sY = 159;
			sX2 = 270;
			sY2 = 215;	
		break;
		case 5:
			sX = 270;
			sY = 180;
			sX2 = 270;
			sY2 = 205;
		break;
	}	
	print.SetColumn(sX, sY, 160, 100);		
	print.printPVR(g_pszInputAimOnSpot[g_GameSettings.m_cActiveLanguage]);	
	print.SetColumn(sX2, sY2, 170, 100);
	print.printPVR(g_pszInputHold[g_GameSettings.m_cActiveLanguage]);	

	// Lock-On
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 219;
			sY = 225;
			sX2 = 219;
			sY2 = 244;			
		break;
		case 1:			
			sX = 230;
			sY = 215;
			sX2 = 230;
			sY2 = 234;			
		break;
		case 2:			
			sX = 219;
			sY = 225;
			sX2 = 219;
			sY2 = 244;			
		break;
		case 3:			
			sX = 219 + 15;
			sY = 229;
			sX2 = 219 + 15;
			sY2 = 249;			
		break;
		case 4:
			if (bAlt)
			{
				sX = 245;
				sY = 235;
				sX2 = 241;
				sY2 = 254;
			}
			else
			{
				sX = 235;
				sY = 235;
				sX2 = 270;
				sY2 = 254;	
			}
		break;
		case 5:
			sX = 235;
			
			if (bAlt)
			{			
				sY = 240;
			}
			else
			{
				sY = 230;
			}

			sX2 = 240;
			sY2 = 270;
		break;
	}	
	print.SetColumn(sX, sY, 200, 200);

	if (bAlt)
	{
		sX += 3;
		print.printPVR(g_pszInputDuck[g_GameSettings.m_cActiveLanguage]);
	}
	else
	{
		print.printPVR(g_pszInputLockOn[g_GameSettings.m_cActiveLanguage]);
	}
		
	print.SetColumn(sX2, sY2, 170, 100);
	print.printPVR(g_pszInputHold[g_GameSettings.m_cActiveLanguage]);	

	// Suicide
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 192;
			sY = 165;
			sX2 = 192;
			sY2 = 180;	
		break;
		case 1:			
			sX = 202;
			sY = 177;
			sX2 = 222;
			sY2 = 192;			
		break;
		case 2:			
			sX = 192;
			sY = 165;
			sX2 = 192;
			sY2 = 180;			
		break;
		case 3:			
			sX = 192 + 15;
			sY = 165;
			sX2 = 192 + 15;
			sY2 = 180;			
		break;
		case 4:
			sX = 210;
			sY = 175;
			sX2 = 210;
			sY2 = 190;	
		break;
		case 5:
			sX = 217;
			sY = 179;
			sX2 = 192;
			sY2 = 180;			
		break;
	}
	print.SetColumn(sX, sY, 170, 100);
	print.printPVR(g_pszInputSuicide[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX2, sY2, 170, 100);
	print.printPVR(g_pszInputHold[g_GameSettings.m_cActiveLanguage]);	
	
	// Move		
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 122;
			sY = 162;			
		break;
		case 1:			
			sX = 152;
			sY = 162;			
		break;
		case 2:			
			sX = 122;
			sY = 162;			
		break;
		case 3:			
			sX = 142;
			sY = 162;			
		break;
		case 4:
			sX = 142;
			sY = 162;			
		break;
		case 5:
			sX = 142;
			sY = 162;			
		break;
	}
	print.SetColumn(sX, sY, 100,100);					
	print.printPVR(g_pszInputMove[g_GameSettings.m_cActiveLanguage]);
	
	// Pause
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 185;
			sY = 279;			
		break;
		case 1:			
			sX = 209;
			sY = 279;			
		break;
		case 2:			
			sX = 192;
			sY = 279;			
		break;
		case 3:			
			sX = 207;
			sY = 279;			
		break;
		case 4:
			sX = 212;
			sY = 279;		
		break;
		case 5:
			sX = 206;
			sY = 279;			
		break;
	}

	print.SetColumn(sX, sY, 200,100);			
	print.printPVR(g_pszInputPause[g_GameSettings.m_cActiveLanguage]);

	// Prev weapon
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 117;
			sY = 210;
			sX2 = 117;
			sY2 = 225;
		break;
		case 1:			
			sX = 147;
			sY = 210;
			sX2 = 147;
			sY2 = 225;
		break;
		case 2:			
			sX = 130;
			sY = 210;
			sX2 = 130;
			sY2 = 225;
		break;
		case 3:			
			sX = 145;
			sY = 210;
			sX2 = 145;
			sY2 = 225;
		break;
		case 4:
			sX = 150;
			sY = 210;
			sX2 = 150;
			sY2 = 225;
		break;
		case 5:
			sX = 130;
			sY = 210;
			sX2 = 150;
			sY2 = 225;
		break;
	}
	print.SetColumn(sX, sY, 100, 100);				
	print.printPVR(g_pszInputPrev[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX2, sY2, 100, 100);				
	print.printPVR(g_pszInputWeapon[g_GameSettings.m_cActiveLanguage]);

	// Next weapon
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 174;
			sY = 210;
			sX2 = 174;
			sY2 = 225;
		break;
		case 1:			
			sX = 194;
			sY = 210;
			sX2 = 194;
			sY2 = 225;
		break;
		case 2:			
			sX = 181;
			sY = 210;
			sX2 = 181;
			sY2 = 225;
		break;
		case 3:			
			sX = 196;
			sY = 210;
			sX2 = 196;
			sY2 = 225;
		break;
		case 4:
			sX = 197;
			sY = 210;
			sX2 = 197;
			sY2 = 225;
		break;
		case 5:
			sX = 196;
			sY = 210;
			sX2 = 196;
			sY2 = 225;
		break;
	}	
	print.SetColumn(sX, sY, 100, 100);				
	print.printPVR(g_pszInputNext[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX2, sY2, 100, 100);	
	print.printPVR(g_pszInputWeapon[g_GameSettings.m_cActiveLanguage]);

	// Grenade / Molotov
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 144;
			sY = 194;
			sX2 = 144;
			sY2 = 247;
		break;
		case 1:			
			sX = 169;
			sY = 194;
			sX2 = 169;
			sY2 = 247;
		break;
		case 2:			
			sX = 150;
			sY = 194;
			sX2 = 150;
			sY2 = 247;
		break;
		case 3:			
			sX = 165;
			sY = 194;
			sX2 = 170;
			sY2 = 247;
		break;
		case 4:
			sX = 160;
			sY = 194;
			sX2 = 170;
			sY2 = 247;
		break;
		case 5:
			sX = 167;
			sY = 194;
			sX2 = 167;
			sY2 = 247;
		break;
	}
	print.SetColumn(sX, sY, 100, 100);				
	print.printPVR(g_pszInputGrenade[g_GameSettings.m_cActiveLanguage]);

	print.SetColumn(sX2, sY2, 100, 100);
	print.printPVR(g_pszInputMolotov[g_GameSettings.m_cActiveLanguage]);

	print.SetColumn(40, 347, 500, 300);
	if (bAlt)
	{
		print.printPVR(g_pszInputAssistedAltTips[g_GameSettings.m_cActiveLanguage]);
	}
	else
	{
		print.printPVR(g_pszInputAssistedTips[g_GameSettings.m_cActiveLanguage]);
	}		
}


static void ControlOptionsWingman(RGuiImage *pgi, bool bAlt = false)
{	
	pgi->heightPvr = 220;
	pgi->widthPvr = 220;
	pgi->startUPvr = 0.f;
	pgi->startVPvr = 0.f;		
	pgi->endUPvr = 0.531f;
	pgi->endVPvr = 0.548f;

	RPrint print;			
	print.SetDestGuiTextString(pgi->m_pGuiTextString);
	print.SetFont(19, &g_fontBig);		
	print.SetColorRGB(255, 0, 0);			
	print.SetShadowColorRGB(128, 0, 0);	
	print.SetEffectAbs(RPrint::SHADOW_X, 1);
	print.SetEffectAbs(RPrint::SHADOW_Y, 1);
	print.SetWordWrap(TRUE);
		
	int16_t sX;
	int16_t sY;
	int16_t sX2;
	int16_t sY2;
	int16_t sX3;
	int16_t sY3;

	// Duck			
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:
			pgi->offsetPvrX = 60;
			sX = 125;
			sY = 105;					
			sX2 = 148;
			sY2 = 105;			
		break;
		case 1:
			pgi->offsetPvrX = 40;
			sX = 155;
			sY = 95;
			sX2 = 155;
			sY2 = 115;
		break;
		case 2:
			pgi->offsetPvrX = 90;
			sX = 125;
			sY = 105;
			sX2 = 125;
			sY2 = 125;
		break;
		case 3:
			pgi->offsetPvrX = 107;
			sX = 145;
			sY = 95;
			sX2 = 145;
			sY2 = 115;
		break;
		case 4:
			pgi->offsetPvrX = 50;			
			if (bAlt)
			{
				sX = 155;
				sY = 95;
				sX2 = 155;
				sY2 = 115;
			}
			else
			{
				sX = 155;
				sY = 95;
				sX2 = 155;
				sY2 = 115;
			}
		break;
		case 5:			
			pgi->offsetPvrX = 95;
			sX = 145;
			sY = 100;
			sX2 = 135;
			sY2 = 120;
		break;
	}
	
	if (bAlt)
	{ 
		print.SetColumn(sX, sY, 170, 100);
		print.printPVR(g_pszInputFire[g_GameSettings.m_cActiveLanguage]);
		print.SetColumn(sX2, sY2, 170, 100);
		print.printPVR(g_pszInputHold[g_GameSettings.m_cActiveLanguage]);
	}
	else
	{
		print.SetColumn(sX, sY, 170, 100);
		print.printPVR(g_pszInputDuck[g_GameSettings.m_cActiveLanguage]);
		print.SetColumn(sX2, sY2, 170, 100);
		print.printPVR(g_pszInputHold[g_GameSettings.m_cActiveLanguage]);
	}
	

	// Fire
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 229;
			sY = 105;
			sX2 = 257;
			sY2 = 105;
		break;
		case 1:			
			sX = 259;
			sY = 95;
			sX2 = 259;
			sY2 = 115;
		break;
		case 2:			
			sX = 229;
			sY = 105;
			sX2 = 229;
			sY2 = 125;
		break;
		case 3:			
			sX = 249;
			sY = 95;
			sX2 = 249;
			sY2 = 115;
		break;
		case 4:
			sX = 254;
			sY = 95;
			sX2 = 254;
			sY2 = 115;
		break;
		case 5:
			sX = 245;
			sY = 100;
			sX2 = 245;
			sY2 = 120;
		break;
	}

	if (bAlt)
	{
		print.SetColumn(sX, sY, 170, 100);
		print.printPVR(g_pszInputDuck[g_GameSettings.m_cActiveLanguage]);		
		print.SetColumn(sX2, sY2, 170, 100);
		print.printPVR(g_pszInputHold[g_GameSettings.m_cActiveLanguage]);
	}
	else
	{
		print.SetColumn(sX, sY, 170, 100);
		print.printPVR(g_pszInputFire[g_GameSettings.m_cActiveLanguage]);
		print.SetColumn(sX2, sY2, 170, 100);
		print.printPVR(g_pszInputHold[g_GameSettings.m_cActiveLanguage]);
	}

	// Aim up
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 240;
			sY = 155;		
		break;
		case 1:			
			sX = 255;
			sY = 138;			
		break;
		case 2:			
			sX = 235;
			sY = 155;			
		break;
		case 3:			
			if (bAlt)
			{
				sX = 255;
				sY = 155;
			}
			else
			{
				sX = 255;
				sY = 145;
			}			
		break;
		case 4:
			if (bAlt)
			{
				sX = 245;
				sY = 150;
			}
			else
			{	
				sX = 230;
				sY = 150;	
			}
				
		break;
		case 5:
			if (bAlt)
			{
				sX = 252;
				sY = 152;
			}
			else
			{
				sX = 245;
				sY = 152;
			}
		break;
	}
	print.SetColumn(sX, sY, 250, 100);		
	
	if (bAlt)
	{
		print.printPVR(g_pszInputMoveUp[g_GameSettings.m_cActiveLanguage]);
	}	
	else
	{
		print.printPVR(g_pszInputAimUp[g_GameSettings.m_cActiveLanguage]);
	}
				
	// Aim right
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 249;
			sY = 190;			
		break;
		case 1:			
			sX = 270;
			sY = 180;
		break;
		case 2:			
			sX = 256;
			sY = 184;			
		break;
		case 3:		
			if (bAlt)
			{
				sX = 272;
				sY = 189;
			}	
			else
			{
				sX = 266;
				sY = 184;
			}			
		break;
		case 4:
			if (bAlt)
			{
				sX = 274;
				sY = 188;
			}
			else
			{	
				sX = 274;
				sY = 180;
			}
				
						
		break;
		case 5:
			if (bAlt)
			{
				sX = 275;
				sY = 185;
			}
			else
			{
				sX = 275;
				sY = 180;
			}			
		break;
	}
	print.SetColumn(sX, sY, 160, 100);		
	if (bAlt)
	{
		print.printPVR(g_pszInputMoveRight[g_GameSettings.m_cActiveLanguage]);
	}	
	else
	{
		print.printPVR(g_pszInputAimRight[g_GameSettings.m_cActiveLanguage]);
	}
	
	// Aim down
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 219;
			sY = 225;		
		break;
		case 1:			
			sX = 240;
			sY = 225;			
		break;
		case 2:			
			sX = 219;
			sY = 225;			
		break;
		case 3:			
			sX = 249;
			sY = 225;	
		break;
		case 4:
			sX = 245;
			sY = 230;		
		break;
		case 5:
			if (bAlt)
			{
				sX = 250;
				sY = 225;
			}
			else
			{
				sX = 240;
				sY = 230;	
			}
		break;
	}	
	print.SetColumn(sX, sY, 200, 200);
	if (bAlt)
	{
		print.printPVR(g_pszInputMoveDown[g_GameSettings.m_cActiveLanguage]);
	}	
	else
	{
		print.printPVR(g_pszInputAimDown[g_GameSettings.m_cActiveLanguage]);
	}
	
	// Aim left
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 189;
			sY = 170;			
		break;
		case 1:			
			sX = 225;
			sY = 160;			
		break;
		case 2:			
			sX = 197;
			sY = 165;			
		break;
		case 3:			
			if (bAlt)
			{
				sX = 192 + 15;
				sY = 170;			
			}
			else
			{
				sX = 192 + 15;
				sY = 165;			
			}
		break;
		case 4:
			if (bAlt)
			{
				sX = 220;
				sY = 180;		
			}
			else
			{
				sX = 210;
				sY = 170;			
			}
				
		break;
		case 5:
			sX = 212;
			sY = 165;			
		break;
	}
	print.SetColumn(sX, sY, 100, 100);
	if (bAlt)
	{
		print.printPVR(g_pszInputMoveLeft[g_GameSettings.m_cActiveLanguage]);
	}	
	else
	{
		print.printPVR(g_pszInputAimLeft[g_GameSettings.m_cActiveLanguage]);
	}

	// Move
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 122;
			sY = 162;			
		break;
		case 1:			
			sX = 152;
			sY = 162;			
		break;
		case 2:			
			sX = 122;
			sY = 162;			
		break;
		case 3:			
			sX = 142;
			sY = 162;			
		break;
		case 4:
			sX = 150;
			sY = 162;			
		break;
		case 5:
			sX = 142;
			sY = 162;			
		break;
	}
	print.SetColumn(sX, sY, 100,100);					
	if (bAlt)
	{
		print.printPVR(g_pszInputAim[g_GameSettings.m_cActiveLanguage]);
	}	
	else
	{
		print.printPVR(g_pszInputMove[g_GameSettings.m_cActiveLanguage]);
	}
	
	// Pause
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 185;
			sY = 279;			
		break;
		case 1:			
			sX = 209;
			sY = 279;			
		break;
		case 2:			
			sX = 192;
			sY = 279;			
		break;
		case 3:			
			sX = 207;
			sY = 279;			
		break;
		case 4:
			sX = 212;
			sY = 279;		
		break;
		case 5:
			sX = 206;
			sY = 279;			
		break;
	}
	print.SetColumn(sX, sY, 200,100);			
	print.printPVR(g_pszInputPause[g_GameSettings.m_cActiveLanguage]);

		// Prev weapon
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 117;
			sY = 210;
			sX2 = 117;
			sY2 = 225;
		break;
		case 1:			
			sX = 147;
			sY = 210;
			sX2 = 147;
			sY2 = 225;
		break;
		case 2:			
			sX = 130;
			sY = 210;
			sX2 = 130;
			sY2 = 225;
		break;
		case 3:			
			sX = 145;
			sY = 210;
			sX2 = 145;
			sY2 = 225;
		break;
		case 4:
			sX = 150;
			sY = 210;
			sX2 = 150;
			sY2 = 225;
		break;
		case 5:
			sX = 130;
			sY = 210;
			sX2 = 150;
			sY2 = 225;
		break;
	}
	print.SetColumn(sX, sY, 100, 100);				
	print.printPVR(g_pszInputPrev[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX2, sY2, 100, 100);				
	print.printPVR(g_pszInputWeapon[g_GameSettings.m_cActiveLanguage]);

	// Next weapon
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 174;
			sY = 210;
			sX2 = 174;
			sY2 = 225;
		break;
		case 1:			
			sX = 194;
			sY = 210;
			sX2 = 194;
			sY2 = 225;
		break;
		case 2:			
			sX = 181;
			sY = 210;
			sX2 = 181;
			sY2 = 225;
		break;
		case 3:			
			sX = 196;
			sY = 210;
			sX2 = 196;
			sY2 = 225;
		break;
		case 4:
			sX = 197;
			sY = 210;
			sX2 = 197;
			sY2 = 225;
		break;
		case 5:
			sX = 196;
			sY = 210;
			sX2 = 196;
			sY2 = 225;
		break;
	}
	print.SetColumn(sX, sY, 100, 100);				
	print.printPVR(g_pszInputNext[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX2, sY2, 100, 100);	
	print.printPVR(g_pszInputWeapon[g_GameSettings.m_cActiveLanguage]);

	// Execute
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 146;
			sY = 194;
			sX2 = 149;
			sY2 = 247;
			sX3 = 149;
			sY3 = 265;
		break;
		case 1:			
			sX = 164;
			sY = 194;
			sX2 = 164;
			sY2 = 247;
			sX3 = 164;
			sY3 = 265;
		break;
		case 2:			
			sX = 150;
			sY = 194;
			sX2 = 150;
			sY2 = 247;
			sX3 = 140;
			sY3 = 265;
		break;
		case 3:			
			sX = 165;
			sY = 194;
			sX2 = 170;
			sY2 = 247;
			sX3 = 160;
			sY3 = 265;
		break;
		case 4:
			sX = 160;
			sY = 194;
			sX2 = 165;
			sY2 = 247;
			sX3 = 165;
			sY3 = 265;
		break;
		case 5:
			sX = 170;
			sY = 194;
			sX2 = 170;
			sY2 = 247;
			sX3 = 167;
			sY3 = 265;
		break;
	}
	print.SetColumn(sX, sY, 190, 100);				
	print.printPVR(g_pszInputExecute[g_GameSettings.m_cActiveLanguage]);

	print.SetColumn(sX2, sY2, 170, 100);
	print.printPVR(g_pszInputSuicide[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX3, sY3, 170, 100);
	print.printPVR(g_pszInputHold[g_GameSettings.m_cActiveLanguage]);

	print.SetColumn(40, 347, 500, 300);
	print.printPVR(g_pszInputWingmanTips[g_GameSettings.m_cActiveLanguage]);		
}

static void ControlOptionsTwinstick(RGuiImage *pgi)
{
	pgi->heightPvr = 174;
	pgi->widthPvr = 270;		
	pgi->startUPvr = 0.f;
	pgi->startVPvr = 0.566f;		
	pgi->endUPvr = 0.636f;
	pgi->endVPvr = 0.992f;

	RPrint print;			
	print.SetDestGuiTextString(pgi->m_pGuiTextString);
	print.SetFont(19, &g_fontBig);		
	print.SetColorRGB(255, 0, 0);			
	print.SetShadowColorRGB(128, 0, 0);	
	print.SetEffectAbs(RPrint::SHADOW_X, 1);
	print.SetEffectAbs(RPrint::SHADOW_Y, 1);
	print.SetWordWrap(TRUE);

	int16_t sX;
	int16_t sY;
	int16_t sX2;
	int16_t sY2;
	int16_t sX3;
	int16_t sY3;

	
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:
			pgi->offsetPvrX = 60;
			sX = 245;
			sY = 135;					
			sX2 = 280;
			sY2 = 135;		 	
			sX3 = 245;
			sY3 = 150;
		break;
		case 1:
			pgi->offsetPvrX = 40;
			sX = 210;
			sY = 135;					
			sX2 = 260;
			sY2 = 135;			
			sX3 = 210;
			sY3 = 150;
		break;
		case 2:
			pgi->offsetPvrX = 90;
			sX = 210;
			sY = 135;					
			sX2 = 252;
			sY2 = 135;			
			sX3 = 257;
			sY3 = 135;
		break;
		case 3:
			pgi->offsetPvrX = 107;
			sX = 210;
			sY = 135;					
			sX2 = 252;
			sY2 = 135;			
			sX3 = 257;
			sY3 = 135;
		break;
		case 4:
			pgi->offsetPvrX = 50;			
			sX = 200;
			sY = 135;					
			sX2 = 246;
			sY2 = 135;			
			sX3 = 250;
			sY3 = 135;
		break;
		case 5:			
			pgi->offsetPvrX = 95;
			sX = 207;
			sY = 135;					
			sX2 = 245;
			sY2 = 135;			
			sX3 = 250;
			sY3 = 135;
		break;
	}

	print.SetColumn(sX, sY, 250, 100);		
	print.printPVR(g_pszInputExecute[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX2, sY2, 250, 100);
	print.printPVR("/");
	print.SetColumn(sX3, sY3, 250, 100);		
	print.printPVR(g_pszInputRevive[g_GameSettings.m_cActiveLanguage]);
	
	
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 274;
			sY = 150;					
			sX2 = 276;
			sY2 = 150;		 	
			sX3 = 299;
			sY3 = 150;
		break;
		case 1:
			sX = 274;
			sY = 150;					
			sX2 = 276;
			sY2 = 150;		 	
			sX3 = 299;
			sY3 = 150;
		break;
		case 2:
			sX = 230;
			sY = 150;					
			sX2 = 232;
			sY2 = 150;		 	
			sX3 = 280;
			sY3 = 150;
		break;
		case 3:
			sX = 240;
			sY = 150;					
			sX2 = 242;
			sY2 = 150;		 	
			sX3 = 297;
			sY3 = 150;
		break;
		case 4:
			sX = 274;
			sY = 150;					
			sX2 = 276;
			sY2 = 150;		 	
			sX3 = 299;
			sY3 = 150;
		break;
		case 5:			
			sX = 269;
			sY = 150;					
			sX2 = 271;
			sY2 = 150;		 	
			sX3 = 294;
			sY3 = 150;
		break;
	}
	print.SetColumn(sX, sY, 250, 100);
	print.printPVR("(");
	print.SetColumn(sX2, sY2, 250, 100);
	print.printPVR(g_pszInputCoop[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX3, sY3, 250, 100);
	print.printPVR(")");

	// Fire	
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 220;
			sY = 170;					
			sX2 = 220;
			sY2 = 185;			
		break;
		case 1:
			sX = 235;
			sY = 170;					
			sX2 = 235;
			sY2 = 185;
		break;
		case 2:
			sX = 220;
			sY = 170;					
			sX2 = 220;
			sY2 = 185;
		break;
		case 3:
			sX = 220;
			sY = 170;					
			sX2 = 220;
			sY2 = 185;
		break;
		case 4:
			sX = 240;
			sY = 170;					
			sX2 = 240;
			sY2 = 185;
		break;
		case 5:			
			sX = 235;
			sY = 170;					
			sX2 = 235;
			sY2 = 185;
		break;
	}
	print.SetColumn(sX, sY, 160, 100);
	print.printPVR(g_pszInputFire[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX2, sY2, 160, 100);
	print.printPVR(g_pszInputHold[g_GameSettings.m_cActiveLanguage]);

	// Duck
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 143;
			sY = 170;					
			sX2 = 143;
			sY2 = 185;			
		break;
		case 1:
			sX = 158;
			sY = 170;					
			sX2 = 158;
			sY2 = 185;			
		break;
		case 2:
			sX = 135;
			sY = 170;					
			sX2 = 135;
			sY2 = 185;			
		break;
		case 3:
			sX = 143;
			sY = 170;					
			sX2 = 143;
			sY2 = 185;			
		break;
		case 4:
			sX = 163;
			sY = 170;					
			sX2 = 163;
			sY2 = 185;			
		break;
		case 5:			
			sX = 163;
			sY = 170;					
			sX2 = 163;
			sY2 = 185;	
		break;
	}
	print.SetColumn(sX, sY, 140, 100);				
	print.printPVR(g_pszInputDuck[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX2, sY2, 140, 100);				
	print.printPVR(g_pszInputHold[g_GameSettings.m_cActiveLanguage]);



	// Prev weapon
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 120;
			sY = 210;					
			sX2 = 120;
			sY2 = 225;			
		break;
		case 1:
			sX = 180;
			sY = 210;					
			sX2 = 180;
			sY2 = 225;			
		break;
		case 2:
			sX = 160;
			sY = 210;					
			sX2 = 160;
			sY2 = 225;				
		break;
		case 3:
			sX = 175;
			sY = 210;					
			sX2 = 175;
			sY2 = 225;			
		break;
		case 4:
			sX = 180;
			sY = 210;					
			sX2 = 180;
			sY2 = 225;			
		break;
		case 5:			
			sX = 170;
			sY = 210;					
			sX2 = 170;
			sY2 = 225;	
		break;
	}
	print.SetColumn(sX, sY, 130, 100);				
	print.printPVR(g_pszInputPrev[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX2, sY2, 130, 100);				
	print.printPVR(g_pszInputWeapon[g_GameSettings.m_cActiveLanguage]);

	// Next weapon
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 259;
			sY = 210;					
			sX2 = 259;
			sY2 = 225;			
		break;
		case 1:
			sX = 259;
			sY = 210;					
			sX2 = 259;
			sY2 = 225;			
		break;
		case 2:
			sX = 244;
			sY = 210;					
			sX2 = 244;
			sY2 = 225;				
		break;
		case 3:
			sX = 250;
			sY = 210;					
			sX2 = 250;
			sY2 = 225;
		break;
		case 4:
			sX = 254;
			sY = 210;					
			sX2 = 254;
			sY2 = 225;			
		break;
		case 5:			
			sX = 254;
			sY = 210;					
			sX2 = 254;
			sY2 = 225;			
		break;
	}
	print.SetColumn(sX, sY, 160, 100);				
	print.printPVR(g_pszInputNext[g_GameSettings.m_cActiveLanguage]);
	print.SetColumn(sX2, sY2, 160, 100);
	print.printPVR(g_pszInputWeapon[g_GameSettings.m_cActiveLanguage]);
		

	// Pause	
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 194;
			sY = 290;											
		break;
		case 1:
			sX = 220;
			sY = 290;					
		break;
		case 2:
			sX = 202;
			sY = 290;					
		break;
		case 3:
			sX = 216;
			sY = 290;					
		break;
		case 4:
			sX = 219;
			sY = 290;					
		break;
		case 5:			
			sX = 217;
			sY = 290;
		break;
	}
	print.SetColumn(sX, sY, 200,100);				
	print.printPVR(g_pszInputPause[g_GameSettings.m_cActiveLanguage]);
	
	// Move
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 158;
			sY = 260;											
		break;
		case 1:
			sX = 180;
			sY = 260;					
		break;
		case 2:
			sX = 150;
			sY = 260;					
		break;
		case 3:
			sX = 169;
			sY = 260;					
		break;
		case 4:
			sX = 179;
			sY = 260;					
		break;
		case 5:			
			sX = 181;
			sY = 260;
		break;
	}
	print.SetColumn(sX, sY, 100, 100);
	print.printPVR(g_pszInputMove[g_GameSettings.m_cActiveLanguage]);	

	// Aim
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 0:			
			sX = 237;
			sY = 260;											
		break;
		case 1:
			sX = 257;
			sY = 260;															
		break;
		case 2:
			sX = 242;
			sY = 260;											
		break;
		case 3:
			sX = 247;
			sY = 260;											
		break;
		case 4:
			sX = 257;
			sY = 260;											
		break;
		case 5:			
			sX = 247;
			sY = 260;											
		break;
	}
	print.SetColumn(sX, sY, 200, 200);
	print.printPVR(g_pszInputAim[g_GameSettings.m_cActiveLanguage]);

	print.SetColumn(40, 347, 500, 300);
	print.printPVR(g_pszInputTwinStickTips[g_GameSettings.m_cActiveLanguage]);

	
}

// todo
static void MultiPlayerOptions(RTxt *pgi, int16_t enabledOptions)
{
	RPrint print;	
	char itemText[20];

	print.SetDestGuiTextString(pgi->m_pGuiTextString);
	print.SetFont(ITEM_FONT_HEIGHT, &g_fontPostal);		
	print.SetEffectAbs(RPrint::SHADOW_X, 1);
	print.SetEffectAbs(RPrint::SHADOW_Y, 1);
	print.SetWordWrap(TRUE);		
	print.SetColumn(0, 0, 250, 100);

	if (g_GameSettings.m_cActiveLanguage == 5)
	{
		print.SetFont(26, &g_fontPostal);		
	}

	if (enabledOptions & PLAYER_1)
	{
		print.SetColorRGB(160, 0, 0);			
		print.SetShadowColorRGB(64, 16, 16);
	}
	else
	{
		print.SetColorRGB(64, 16, 16);
		print.SetShadowColorRGB(160, 0, 0);					
	}
		
	print.offsetX = -150;
	print.offsetY = -2;
	
	sprintf(itemText, "%s 1", g_pszPlayer[g_GameSettings.m_cActiveLanguage]);
	print.printPVR(itemText);
		
	// Player 2
	if (enabledOptions & PLAYER_2)
	{		
		print.SetColorRGB(160, 0, 0);			
		print.SetShadowColorRGB(64, 16, 16);
	}
	else
	{
		print.SetColorRGB(64, 16, 16);
		print.SetShadowColorRGB(160, 0, 0);					
	}
		
	print.offsetX = -150;
	print.offsetY = 30;
	sprintf(itemText, "%s 2", g_pszPlayer[g_GameSettings.m_cActiveLanguage]);
	print.printPVR(itemText);

	// Player 3
	if (enabledOptions & PLAYER_3)
	{
		print.SetColorRGB(160, 0, 0);			
		print.SetShadowColorRGB(64, 16, 16);
	}
	else
	{
		print.SetColorRGB(64, 16, 16);
		print.SetShadowColorRGB(160, 0, 0);					
	}
		
	print.offsetX = -150;
	print.offsetY = 63;
	sprintf(itemText, "%s 3", g_pszPlayer[g_GameSettings.m_cActiveLanguage]);
	print.printPVR(itemText);

	// Player 4
	if (enabledOptions & PLAYER_4)
	{
		print.SetColorRGB(160, 0, 0);			
		print.SetShadowColorRGB(64, 16, 16);
	}
	else
	{
		print.SetColorRGB(64, 16, 16);
		print.SetShadowColorRGB(160, 0, 0);					
	}
		
	print.offsetX = -150;
	print.offsetY = 96;
	sprintf(itemText, "%s 4", g_pszPlayer[g_GameSettings.m_cActiveLanguage]);
	print.printPVR(itemText);

	// Start campaign
	if(g_sActivePlayers > PLAYER_1)
	{
		print.SetColorRGB(160, 0, 0);			
		print.SetShadowColorRGB(64, 16, 16);
	}
	else
	{
		print.SetColorRGB(64, 16, 16);
		print.SetShadowColorRGB(160, 0, 0);					
	}
		
	print.offsetX = -150;
	print.offsetY = 162;
	print.printPVR(g_pszCoopCampaign[g_GameSettings.m_cActiveLanguage]);
}

////////////////////////////////////////////////////////////////////////////////
// Menu callbacks.

static int16_t MainMenuInit( // Returns 0 on succes, non-zero to cancel menu.
	Menu *pmenuCurrent,		 // In:  Menu being init'ed or killed.
	int16_t sInit);			 // In:  TRUE, if initializing; FALSE, if killing.

static bool MainMenuChoice(		  // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,			  // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);			  // Item chosen.

static int16_t ClientGameMenuInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit);

static bool ClientGameMenuChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,			  // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);			  // Item chosen.

static bool NewCampaignMenu( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		 // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);		 // Item chosen.

static int16_t NewCampaignInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit);				// TRUE, if initializing; FALSE, if killing.

static bool NewCampaignCoopMenu( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		 // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);		 // Item chosen.

static int16_t NewCampaignCoopInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit);				// TRUE, if initializing; FALSE, if killing.

static bool StartSingleMenu( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		 // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);		 // Item chosen.

static int16_t ChallengeInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			  // Current menu.
	int16_t sInit);			  // TRUE, if initializing; FALSE, if killing.

static bool ChallengeChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		 // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);		 // Item chosen.

static bool ChallengeTimedChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,			  // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);			  // Item chosen.

static bool ChallengeGoalChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,			 // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);			 // Item chosen.

static bool ChallengeFlagChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,			 // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);			 // Item chosen.

static bool ChallengeCheckpointChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,				   // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);				   // Item chosen.

static int16_t StartSingleInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit);				// TRUE, if initializing; FALSE, if killing.

static int16_t OptionsInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			// Current menu.
	int16_t sInit);			// TRUE, if initializing; FALSE, if killing.

static bool OptionsChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,	   // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);	   // Item chosen.

static int16_t GameOptionsInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit);				// TRUE, if initializing; FALSE, if killing.

static bool GameOptionsChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		   // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);		   // Item chosen.

static int16_t PlayOptionsInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit);				// TRUE, if initializing; FALSE, if killing.

static bool PlayOptionsChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		   // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);		   // Item chosen.

static int16_t AudioOptionsInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				 // Current menu.
	int16_t sInit);				 // TRUE, if initializing; FALSE, if killing.

static bool AudioOptionsChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,			// Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);			// Item chosen.

static int16_t VolumesInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			// Current menu.
	int16_t sInit);			// TRUE, if initializing; FALSE, if killing.

static bool VolumesChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,	   // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);	   // Item chosen.

static int16_t ControlsInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit);			 // TRUE, if initializing; FALSE, if killing.

static bool ControlsChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		// Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);		// Item chosen.

static int16_t ControlsOptionsInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit);			 // TRUE, if initializing; FALSE, if killing.

static bool ControlsOptionsChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		// Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);		// Item chosen.

static int16_t LoadLevelInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			  // Current menu.
	int16_t sInit);			  // TRUE, if initializing; FALSE, if killing.

static bool LoadLevelChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		 // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);

static int16_t MultiOptionsInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				 // Current menu.
	int16_t sInit);				 // TRUE, if initializing; FALSE, if killing.

static bool MultiOptionsChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCur,				// Current menu.
	int16_t sMenuItem, int16_t sMenuCurr);			// Item chosen.

static int16_t VideoMenuInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit);			 // TRUE, if initializing; FALSE, if killing.

static bool VideoMenuChoice( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit, int16_t sMenuCurr);			 // TRUE, if initializing; FALSE, if killing.

static int16_t LanguageMenuInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit);			 // TRUE, if initializing; FALSE, if killing.

static bool LanguageMenuChoice( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit, int16_t sMenuCurr);			 // TRUE, if initializing; FALSE, if killing.

static int16_t NoMemoryCardMenuInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit);			 // TRUE, if initializing; FALSE, if killing.

static bool NoMemoryCardMenuChoice( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit, int16_t sMenuCurr);			 // TRUE, if initializing; FALSE, if killing.

static int16_t PickMemoryCardMenuInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit);			 // TRUE, if initializing; FALSE, if killing.

static bool PickMemoryCardMenuChoice( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit, int16_t sMenuCurr);			 // TRUE, if initializing; FALSE, if killing.

static int16_t PickFileMenuInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit);			 // TRUE, if initializing; FALSE, if killing.

static bool PickFileMenuChoice( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit, int16_t sMenuCurr);			 // TRUE, if initializing; FALSE, if killing.

static int16_t MapleDisconnectMenuInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit);			 // TRUE, if initializing; FALSE, if killing.

static bool MapleDisconnectMenuChoice( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit, int16_t sMenuCurr);			 // TRUE, if initializing; FALSE, if killing.

////////////////////////////////////////////////////////////////////////////////
// GUI callbacks.

static void VolumesScrollUpdate( // Returns nothing.
	RScrollBar *psb);			 // Scrollbar that got updated.

static void PainFrequencyScrollUpdate( // Returns nothing.
	RScrollBar *psb);				   // Scrollbar that got updated.

////////////////////////////////////////////////////////////////////////////////
// Variables/data
////////////////////////////////////////////////////////////////////////////////

CMenuSettings g_MenuSettings;
static MAPLE::cMapleVmu* g_pMapleVmuIndices[MAPLE_MEMCARD_MAX] = {nullptr};
static int16_t g_sSelectedGameMode = -1;
static int16_t g_sSelectedSingleLevel = -1;
static int16_t g_sSelectedChallengeMode = -1;
static int16_t g_sSelectedChallengeLevel = -1;
static int8_t g_cMapleVmuCount = 0;
static int8_t g_PickFileState = 0;
static bool g_bDynamicStringIndices[16] = {false};
static int8_t g_cActiveController = 0;

// Controls.
static RScrollBar *ms_psbGamma = NULL;

static REdit *ms_peditConnect = NULL;
static REdit *ms_peditHostName = NULL;
static REdit *ms_peditName = NULL;
static RTxt *ms_ptxtColor = NULL;

static RTxt *ms_ptxtProto = NULL;
static RTxt *ms_ptxtLanguageAudio = NULL;
static RTxt *ms_ptxtLanguage = NULL;
static RTxt *ms_ptxtInterface = NULL;
static RTxt *ms_ptxtUnderscan = NULL;

static RMultiBtn *ms_pmbCheckBox = NULL;

static RScrollBar *ms_psbDifficulty = NULL;

static RScrollBar *ms_psbMouseSensitivityX = NULL;
static RScrollBar *ms_psbMouseSensitivityY = NULL;

static RTxt *ms_ptxtBandwidth = NULL;

static SampleMaster::SoundInstance ms_siLastSamplePlayed = 0; // Last sample played.

static SampleMasterID *ms_apsmidVolumeTesters[SampleMaster::MAX_NUM_SOUND_CATEGORIES] =
	{
		&g_smidEmptyWeapon,	  // Unspecified
		&g_smidMusicTester,	  // BackgroundMusic
		&g_smidShotgun,		  // Weapon
		&g_smidLoadedWeapon,  // UserFeedBack
		&g_smidRocketExplode, // Destruction
		&g_smidAmbientTester, // Ambient
		&g_smidDemonBleed,	  // Demon.
		&g_smidSteveScumbag,  // Voices.
		&g_smidMikeAhh,		  // Pain
		&g_smidWrithing2,	  // Suffering
};

char levelNames[22][256];
char challengeNames[10][256];
char controllerNames[4][30];

// Main menu
extern Menu menuMain =
	{
		MAIN_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_SM,			  // menu x, y, w, h
			-60,					  // menu header x offset
			MENU_HEAD_Y_SM,			  // menu header y offset
			MENU_ITEM_X_SM,			  // menu items x offset
			MENU_ITEM_Y_SM,			  // menu items y offset
			MENU_ITEM_SPACE_Y_SM,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_SM, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor, sStartIndex, sNumEntries
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{// pszFile, type
		 MENU_INDICATOR,
		 RImage::FSPR8},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			MainMenuInit,  // Called before menu is initialized.
			MainMenuChoice // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			3, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

// Menu items.
		{
			// pszText,						sEnabled,	pmenu,				pgui
			{
				"",
				TRUE,	
				&menuStartSingle,
				NULL,
			},
			{
				"",
				TRUE,
				&menuOptions,
				NULL,
			},

			{
				"",
				TRUE,				
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};

// Client game menu
extern Menu menuClientGame =
	{
		CLIENT_GAME_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_SM,			  // menu x, y, w, h
			-60,					  // menu header x offset
			MENU_HEAD_Y_SM,			  // menu header y offset
			MENU_ITEM_X_SM,			  // menu items x offset
			MENU_ITEM_Y_SM,			  // menu items y offset
			MENU_ITEM_SPACE_Y_SM,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_SM, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			ClientGameMenuInit,		   // Called before menu is initialized.
			Play_VerifyQuitMenuChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0, // Menu item (index in ami[]) selected initially.
			   // Negative indicates distance from number of items
			   // (e.g., -1 is the last item).
			0, // Menu item (index in ami[]) chosen on cancel.
			   // Negative indicates distance from number of items
			   // (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,		sEnabled,	pmenu,						pgui
			{
				"",				
				TRUE,
				NULL,
				NULL,
			},			
			{
				"",
				TRUE,
				&menuOptions,
				NULL,
			},
			{				
				"",
				TRUE,
				NULL,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};

// Options menu.
extern Menu menuOptions =
	{
		OPTIONS_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_MD,			  // menu x, y, w, h
			-60,					  // menu header x offset
			MENU_HEAD_Y_MD,			  // menu header y offset
			MENU_ITEM_X_MD,			  // menu items x offset
			MENU_ITEM_Y_MD,			  // menu items y offset
			MENU_ITEM_SPACE_Y_MD,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_MD, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			OptionsInit,   // Called before menu is initialized.
			OptionsChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,								sEnabled,	pmenu,					pgui

			{
				"",
				TRUE,
				&menuAudioOptions,
				NULL,
			},
			{
				"",
				TRUE,
				&menuControls,
				NULL,
			},
			{
				"",
				TRUE,
				&menuGameOptions,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};

// Game options menu.
extern Menu menuGameOptions =
	{
		OPTIONS_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_MD,			  // menu x, y, w, h
			-60,					  // menu header x offset
			MENU_HEAD_Y_MD,			  // menu header y offset
			MENU_ITEM_X_MD,			  // menu items x offset
			MENU_ITEM_Y_MD,			  // menu items y offset
			MENU_ITEM_SPACE_Y_MD,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_MD, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			GameOptionsInit,   // Called before menu is initialized.
			GameOptionsChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,								sEnabled,	pmenu,					pgui
			{
				"",
				TRUE,
				NULL,
				NULL,
			},		
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			{
				"UNDERSCAN",
				TRUE,
				NULL,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};

// Options menu.
extern Menu menuPlayOptions =
	{
		PLAYOPTIONS_MENU_ID,

			// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_LG,			  // menu x, y, w, h
			-120,					  // menu header x offset
			MENU_HEAD_Y_LG,			  // menu header y offset
			MENU_ITEM_X_LG,			  // menu items x offset
			MENU_ITEM_Y_LG,			  // menu items y offset
			MENU_ITEM_SPACE_Y_LG,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_LG, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			PlayOptionsInit,   // Called before menu is initialized.
			PlayOptionsChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			1,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},
			// Menu items.
		{
			// pszText,											sEnabled,	pmenu,				pgui
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},			
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},	
};

// Audio Options menu.
extern Menu menuAudioOptions =
	{
		AUDIO_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_MD,			  // menu x, y, w, h
			-60,					  // menu header x offset
			MENU_HEAD_Y_MD,			  // menu header y offset
			MENU_ITEM_X_MD,			  // menu items x offset
			MENU_ITEM_Y_MD,			  // menu items y offset
			MENU_ITEM_SPACE_Y_MD,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_MD, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			AudioOptionsInit,	// Called before menu is initialized.
			AudioOptionsChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,				sEnabled,	pmenu,					pgui
			{
				"",
				TRUE,
				&menuVolumes,
				NULL,
			},			
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};


// Cntrols menu
extern Menu menuControls =
	{
		CONTROLS_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_MD,			  // menu x, y, w, h
			-60,					  // menu header x offset
			MENU_HEAD_Y_MD,			  // menu header y offset
			MENU_ITEM_X_MD,			  // menu items x offset
			MENU_ITEM_Y_MD,			  // menu items y offset
			MENU_ITEM_SPACE_Y_MD,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_MD, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			ControlsInit,	// Called before menu is initialized.
			ControlsChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,				sEnabled,	pmenu,					pgui
			{
				"", // todo: localisation
				TRUE,
				&menuControllerOptions,
				NULL,
			},			
			{
				"", // todo: localisation
				TRUE,
				&menuControllerOptions,
				NULL,
			},
			{
				"", // todo: localisation
				TRUE,
				&menuControllerOptions,
				NULL,
			},
			{
				"", // todo: localisation
				TRUE,
				&menuControllerOptions,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};


// Video options menu.
extern Menu menuVideo =
	{
		VIDEO_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_SM,			  // menu x, y, w, h
			-60,					  // menu header x offset
			MENU_HEAD_Y_SM,			  // menu header y offset
			MENU_ITEM_X_SM,			  // menu items x offset
			MENU_ITEM_Y_SM,			  // menu items y offset
			MENU_ITEM_SPACE_Y_SM,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_SM, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter | MenuColumnizeGuis),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
		MENU_INDICATOR,		
		//NULL,
		RImage::FSPR8,
		//NULL,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			VideoMenuInit,	// Called before menu is initialized.
			VideoMenuChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,												sEnabled,	pmenu,					pgui			
			// pszText,				sEnabled,	pmenu,					pgui
			{
				"50Hz", // TODO: localisation
				TRUE,
				NULL,
				NULL,
			},
			{
				"60Hz", // TODO: localisation
				TRUE,
				NULL,
				NULL,
			},		
			{
				"60Hz*", // TODO: localisation
				TRUE,
				NULL,
				NULL,
			},		
			{
				"*(cutscene", // TODO: localisation
				FALSE,
				NULL,
				NULL,
			},
			{
				"underscan)", // TODO: localisation
				FALSE,
				NULL,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},		
			NULL // Terminates list.
		},
};

// Language options menu.
extern Menu menuLanguage =
	{
		LANGUAGE_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_SM,			  // menu x, y, w, h
			-60,					  // menu header x offset
			MENU_HEAD_Y_SM,			  // menu header y offset
			MENU_ITEM_X_SM,			  // menu items x offset
			MENU_ITEM_Y_SM,			  // menu items y offset
			MENU_ITEM_SPACE_Y_SM,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_SM, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter | MenuColumnizeGuis),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			"LANGUAGE",
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
		MENU_INDICATOR,		
		//NULL,
		RImage::FSPR8,
		//NULL,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			LanguageMenuInit,	// Called before menu is initialized.
			LanguageMenuChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,												sEnabled,	pmenu,					pgui			
			// pszText,				sEnabled,	pmenu,					pgui
			{
				g_pszMenuLanguages[0],			
				TRUE,
				NULL,
				NULL,
			},
			{
				g_pszMenuLanguages[1],
				TRUE,
				NULL,
				NULL,
			},			
			{
				g_pszMenuLanguages[2],
				TRUE,
				NULL,
				NULL,
			},			
			{
				g_pszMenuLanguages[3],
				TRUE,
				NULL,
				NULL,
			},			
			{
				g_pszMenuLanguages[4],
				TRUE,
				NULL,
				NULL,
			},	
			{
				g_pszMenuLanguages[5],
				TRUE,
				NULL,
				NULL,
			},			
			{
				"",
				TRUE,
				NULL,
				NULL,
			},		
			NULL // Terminates list.
		},
};


// Controller options menu.
extern Menu menuControllerOptions =
	{
		JOYSTICK_MENU_ID,
		
		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_LG,			  // menu x, y, w, h
			-60,					  // menu header x offset
			MENU_HEAD_Y_LG,			  // menu header y offset
			MENU_ITEM_X_LG,			  // menu items x offset
			MENU_ITEM_Y_LG,			  // menu items y offset
			MENU_ITEM_SPACE_Y_LG,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_LG, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter | MenuColumnizeGuis),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
		MENU_INDICATOR,		
		//NULL,
		RImage::FSPR8,
		//NULL,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			ControlsOptionsInit,	// Called before menu is initialized.
			ControlsOptionsChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,												sEnabled,	pmenu,					pgui			
			// pszText,				sEnabled,	pmenu,					pgui
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			{
				"",
				FALSE,
				NULL,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};



// Level select.
extern Menu menuLoadLevel =
	{
		LOAD_LEVEL_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_LG, // Pos and dimensions.
			15,			  // Offset from left edge for menu header.
						  // Negative indicates offset from center.
			12,			  // Offset from top edge for menu header.
						  // Negative indicates offset from center.
			72,			  // Offset from left edge for menu items.
						  // Negative indicates offset from center.
			56,			  // Offset from top edge for menu items.
						  // Negative indicates offset from center.
			-5,			  // Space between menu items.
			5,			  // Space between indicator and menu items horizontally.
			-10,		  // X position menu items should not pass w/i Menu.
						  // Less than 1, indicates offset from right edge.
			-90,		  // Y position menu items should not pass w/i Menu.
						  // Less than 1, indicates offset from right edge.
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter | MenuColumnizeGuis),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT_SMALLER, // Height of font.
			ITEM_COLOR,				  // Text RGBA.
			ITEM_SHADOW_COLOR		  // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			LoadLevelInit,	 // Called before menu is initialized.
			LoadLevelChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		// Note that in the old ways we had to reserve as many of these
		// as there were items to be in this menu, but now, since this array
		// is not open ended, we don't.  But, if it ever ends up open ended
		// again, this will have to be updated.  I included enough for the
		// current settings, but more will have to be added, if we ever change
		// back and add more input functions.

		{
			// pszText,				sEnabled,	pmenu,					pgui

			// original game
			{
				levelNames[0],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[1],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[2],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[3],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[4],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[5],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[6],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[7],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[8],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[9],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[10],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[11],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[12],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[13],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[14],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[15],
				TRUE,
				&menuPlayOptions,
				NULL,
			},

			// SD and Plus
			{
				levelNames[16],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[17],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[18],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[19],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[20],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				levelNames[21],
				TRUE,
				&menuPlayOptions,
				NULL,
			},

			{
				"",
				TRUE,
				NULL,
				NULL,
			},	 // This needs to be at the end of the list or ESC will load the last level
			NULL // Terminates list.
		},
};

// Volumes menu.
extern Menu menuVolumes =
	{
		VOLUME_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_MD,			  // menu x, y, w, h
			-60,					  // menu header x offset
			MENU_HEAD_Y_MD,			  // menu header y offset
			MENU_ITEM_X_MD,			  // menu items x offset
			MENU_ITEM_Y_MD,			  // menu items y offset
			1,						  //MENU_ITEM_SPACE_Y_MD,		// vertical space between menu items
			MENU_ITEM_IND_SPACE_X_MD, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter | MenuColumnizeGuis),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT_SMALLER, // Height of font.
			ITEM_COLOR,				  // Text RGBA.
			ITEM_SHADOW_COLOR		  // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			VolumesInit,   // Called before menu is initialized.
			VolumesChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		// Note that in the old ways we had to reserve as many of these
		// as there were items to be in this menu, but now, since this array
		// is not open ended, we don't.  But, if it ever ends up open ended
		// again, this will have to be updated.  I included enough for the
		// current settings, but more will have to be added, if we ever change
		// back and add more volume categories.
		{
			// pszText,				sEnabled,	pmenu,					pgui
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			{
				NULL,
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};

// Single player start menu.
extern Menu menuStartSingle =
	{
		START_SINGLE_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_MD,			  // menu x, y, w, h
			-120,					  // menu header x offset
			MENU_HEAD_Y_MD,			  // menu header y offset
			MENU_ITEM_X_MD,			  // menu items x offset
			MENU_ITEM_Y_MD,			  // menu items y offset
			MENU_ITEM_SPACE_Y_MD,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_MD, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.			
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			StartSingleInit, // Called before menu is initialized.
			StartSingleMenu, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,											sEnabled,	pmenu,				pgui
			{
				"",								
				FALSE,
				NULL,
				NULL,
			},
			{
				"",
				TRUE,
				&menuNewCampaign,
				NULL,
			},		

			{
				"",
				TRUE,
				&menuLoadLevel,
				NULL,
			},
			{
				"",
				TRUE,
				&menuChallenge,
				NULL,
			},
			{
				"",
				FALSE,
				&menuMultiOptions,
				NULL,
			},
			{
				"",
				FALSE,
				&menuPickMemoryCard,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};

// Single player new campaign menu.
extern Menu menuNewCampaign =
	{
		START_SINGLE_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_LG,			  // menu x, y, w, h
			-120,					  // menu header x offset
			MENU_HEAD_Y_LG,			  // menu header y offset
			MENU_ITEM_X_LG,			  // menu items x offset
			MENU_ITEM_Y_LG,			  // menu items y offset
			MENU_ITEM_SPACE_Y_LG,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_LG, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			NewCampaignInit, // Called before menu is initialized.
			NewCampaignMenu, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,											sEnabled,	pmenu,				pgui
			{
				"",
				TRUE,
				&menuPlayOptions,
				NULL,
			},

			{
				"",
				TRUE,
				&menuPlayOptions,
				NULL,
			},

			{
				"",
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				"",
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};


// Single player new campaign menu.
extern Menu menuNewCampaignCoop =
	{
		START_SINGLE_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_LG,			  // menu x, y, w, h
			-120,					  // menu header x offset
			MENU_HEAD_Y_LG,			  // menu header y offset
			MENU_ITEM_X_LG,			  // menu items x offset
			MENU_ITEM_Y_LG,			  // menu items y offset
			MENU_ITEM_SPACE_Y_LG,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_LG, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			NewCampaignCoopInit, // Called before menu is initialized.
			NewCampaignCoopMenu, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,											sEnabled,	pmenu,				pgui
			{
				"",
				FALSE,
				NULL,
				NULL,
			},
			// pszText,											sEnabled,	pmenu,				pgui
			{
				"",
				TRUE,
				&menuPlayOptions,
				NULL,
			},

			{
				"",
				TRUE,
				&menuPlayOptions,
				NULL,
			},

			{
				"",
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				"",
				TRUE,
				&menuPlayOptions,
				NULL,
			},

			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};




// Single player start menu.
extern Menu menuChallenge =
	{
		CHALLENGE_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_MD,			  // menu x, y, w, h
			-120,					  // menu header x offset
			MENU_HEAD_Y_MD,			  // menu header y offset
			MENU_ITEM_X_MD,			  // menu items x offset
			MENU_ITEM_Y_MD,			  // menu items y offset
			MENU_ITEM_SPACE_Y_MD,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_MD, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			ChallengeInit,	 // Called before menu is initialized.
			ChallengeChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,					sEnabled,		pmenu,		pgui
			{
				challengeNames[0],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[1],
				TRUE,
				&menuChallengeTimed,
				NULL,
			},
			{
				challengeNames[2],
				TRUE,
				&menuChallengeGoal,
				NULL,
			},
			{
				challengeNames[3],
				TRUE,
				&menuChallengeFlag,
				NULL,
			},
			{
				challengeNames[4],
				TRUE,
				&menuChallengeCheckpoint,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};

extern Menu menuChallengeTimed =
	{
		CHALLENGE_TIMED_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_MD,			  // menu x, y, w, h
			-120,					  // menu header x offset
			MENU_HEAD_Y_MD,			  // menu header y offset
			MENU_ITEM_X_MD,			  // menu items x offset
			MENU_ITEM_Y_MD,			  // menu items y offset
			MENU_ITEM_SPACE_Y_MD,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_MD, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			ChallengeInit,		  // Called before menu is initialized.
			ChallengeTimedChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0, // Menu item (index in ami[]) selected initially.
			// Negative indicates distance from number of items
			// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
			// Negative indicates distance from number of items
			// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,					sEnabled,		pmenu,		pgui
			{
				challengeNames[0],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[1],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[2],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[3],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[4],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};

extern Menu menuChallengeGoal =
	{
		CHALLENGE_GOAL_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_MD,			  // menu x, y, w, h
			-120,					  // menu header x offset
			MENU_HEAD_Y_MD,			  // menu header y offset
			MENU_ITEM_X_MD,			  // menu items x offset
			MENU_ITEM_Y_MD,			  // menu items y offset
			MENU_ITEM_SPACE_Y_MD,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_MD, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			ChallengeInit,		 // Called before menu is initialized.
			ChallengeGoalChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0, // Menu item (index in ami[]) selected initially.
			// Negative indicates distance from number of items
			// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
			// Negative indicates distance from number of items
			// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,					sEnabled,		pmenu,		pgui
			{
				challengeNames[0],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[1],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[2],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[3],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[4],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[5],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};

extern Menu menuChallengeFlag =
	{
		CHALLENGE_FLAG_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_MD,			  // menu x, y, w, h
			-120,					  // menu header x offset
			MENU_HEAD_Y_MD,			  // menu header y offset
			MENU_ITEM_X_MD,			  // menu items x offset
			MENU_ITEM_Y_MD,			  // menu items y offset
			MENU_ITEM_SPACE_Y_MD,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_MD, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			ChallengeInit,		 // Called before menu is initialized.
			ChallengeFlagChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0, // Menu item (index in ami[]) selected initially.
			// Negative indicates distance from number of items
			// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
			// Negative indicates distance from number of items
			// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,					sEnabled,		pmenu,		pgui
			{
				challengeNames[0],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[1],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[2],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[3],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[4],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[5],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};

extern Menu menuChallengeCheckpoint =
	{
		CHALLENGE_CHECKPOINT_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_MD,			  // menu x, y, w, h
			-120,					  // menu header x offset
			MENU_HEAD_Y_MD,			  // menu header y offset
			MENU_ITEM_X_MD,			  // menu items x offset
			MENU_ITEM_Y_MD,			  // menu items y offset
			MENU_ITEM_SPACE_Y_MD,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_MD, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			ChallengeInit,			   // Called before menu is initialized.
			ChallengeCheckpointChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0, // Menu item (index in ami[]) selected initially.
			// Negative indicates distance from number of items
			// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
			// Negative indicates distance from number of items
			// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,					sEnabled,		pmenu,		pgui
			{
				challengeNames[0],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[1],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[2],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[3],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				challengeNames[4],
				TRUE,
				&menuPlayOptions,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};


// Multiplayer options menu.
extern Menu menuMultiOptions =
	{
		MULTIPLAYER_OPTIONS_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_MD,			  // menu x, y, w, h
			-60,					  // menu header x offset
			MENU_HEAD_Y_MD,			  // menu header y offset
			MENU_ITEM_X_MD,			  // menu items x offset
			MENU_ITEM_Y_MD,			  // menu items y offset
			MENU_ITEM_SPACE_Y_MD,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_MD, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter | MenuColumnizeGuis),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,			
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			MultiOptionsInit,	// Called before menu is initialized.
			MultiOptionsChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		{
			// pszText,				sEnabled,	pmenu,			pgui
			{
				"                ",
				TRUE,
				NULL,
				NULL,
			},
		{
				"                ",
				FALSE,
				NULL,
				NULL,
			},
			{
				"                ",
				FALSE,
				NULL,
				NULL,
			},
			{
				"                ",
				FALSE,
				NULL,
				NULL,
			},
				{
				"                ",
				FALSE,
				NULL,
				NULL,
			},
			{
				"                ",
				FALSE,
				&menuNewCampaignCoop,
				NULL,
			},
			{
				"",
				TRUE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};

////////////////////////////////////////////////////////////////////////////////
// Menu callbacks.

////////////////////////////////////////////////////////////////////////////////
//
// Called when main menu is initialized or killed.
//
////////////////////////////////////////////////////////////////////////////////
static int16_t MainMenuInit( // Returns 0 on succes, non-zero to cancel menu.
	Menu *pmenuCurrent,		 // In:  Menu being init'ed or killed.
	int16_t sInit)			 // In:  TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.

	pmenuCurrent->menuheader.pszHeaderText = g_pszMainMenu_Title[g_GameSettings.m_cActiveLanguage];
	pmenuCurrent->ami[0].pszText = g_pszMainMenu_Start[g_GameSettings.m_cActiveLanguage];
	pmenuCurrent->ami[1].pszText = g_pszMainMenu_Options[g_GameSettings.m_cActiveLanguage];
	pmenuCurrent->ami[2].pszText = g_pszMainMenu_Credits[g_GameSettings.m_cActiveLanguage];
	
	Game_InitMainMenu(sInit);

	return sRes;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called when item is chosen from main menu
//
////////////////////////////////////////////////////////////////////////////////
static bool MainMenuChoice(	 // Returns true to accept, false to deny choice.
	Menu * pmenuCurrent, // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)		 // Item chosen.
{
	bool bAcceptChoice = true; // Assume accepting.
	
	if (cMapleVmu::m_pActiveVmu == nullptr)
	{
		// Init stub game settings.
		Game_InitSaveFile();
		Game_LoadPlayersSaveSlot(0);
	}

	g_sSelectedGameMode = -1;
	g_sSelectedSingleLevel = -1;
	g_sSelectedChallengeMode = -1;
	g_sSelectedChallengeLevel = -1;

	// Audible Feedback.
	if (sMenuItem == -1)
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	else
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);

	switch (sMenuItem)
	{
	case 2:					
		Credits();	
		g_pvrRenderer->m_fGlobalRScale = 1.0f;
		g_pvrRenderer->m_fGlobalGScale = 0.0f;
		g_pvrRenderer->m_fGlobalBScale = 0.0f;
		// Restore the main menu screen background.
		g_pvrRenderer->DrawQuad(
			g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_SPLASH_THIRD),
			0, 0, 640, 480,
			PVR::cRenderMgr::LayerBg, 1.0f
		);
		break;

	case 3:		
		return false;		
		break;
	}

	return bAcceptChoice;
}


static bool ClientGameMenuChoice( // Returns true to accept, false to deny choice.
	Menu * /*pmenuCurrent*/,	  // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)			  // Item chosen.
{
	bool bAcceptChoice = true; // Assume accepting.

	// Audible Feedback.
	if (sMenuItem == -1)
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	else
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);

	switch (sMenuItem)
	{
	case 0: // Continue.
		StopMenu();
		break;
	case 1: // Options.
		break;
	case 2: // Quit.
		break;
	}

	return bAcceptChoice;
}

static int16_t ClientGameMenuInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			  // Current menu.
	int16_t sInit)			  // TRUE, if initializing; FALSE, if killing.
{	
	pmenuCur->menuheader.pszHeaderText = g_pszGameMenu_Title[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[0].pszText = g_pszGameMenu_Continue[g_GameSettings.m_cActiveLanguage];			
	pmenuCur->ami[1].pszText = g_pszGameMenu_Options[g_GameSettings.m_cActiveLanguage];						
	pmenuCur->ami[2].pszText = g_pszGameMenu_Suicide[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[3].pszText = g_pszGameMenu_Quit[g_GameSettings.m_cActiveLanguage];	
	return 0;
}


////////////////////////////////////////////////////////////////////////////////
//
// Called to init or kill the Challenge menu.
//
////////////////////////////////////////////////////////////////////////////////
static int16_t ChallengeInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			  // Current menu.
	int16_t sInit)			  // TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.
	
	//Fill Challenge Names into array.
	switch (pmenuCur->u32Id)
	{
	case 29:
		pmenuCur->menuheader.pszHeaderText = g_pszStartChallengeMenu_Timed[g_GameSettings.m_cActiveLanguage];
		strcpy(challengeNames[0], GetChallengeTitle(10));
		strcpy(challengeNames[1], GetChallengeTitle(4));
		strcpy(challengeNames[2], GetChallengeTitle(20));
		strcpy(challengeNames[3], GetChallengeTitle(7));
		strcpy(challengeNames[4], GetChallengeTitle(9));
		break;
	case 30:		
		pmenuCur->menuheader.pszHeaderText = g_pszStartChallengeMenu_Goal[g_GameSettings.m_cActiveLanguage];
		strcpy(challengeNames[0], GetChallengeTitle(16));
		strcpy(challengeNames[1], GetChallengeTitle(14));
		strcpy(challengeNames[2], GetChallengeTitle(2));
		strcpy(challengeNames[3], GetChallengeTitle(19));
		strcpy(challengeNames[4], GetChallengeTitle(23));
		strcpy(challengeNames[5], GetChallengeTitle(21));
		break;
	case 31:
		pmenuCur->menuheader.pszHeaderText = g_pszStartChallengeMenu_Flag[g_GameSettings.m_cActiveLanguage];
		strcpy(challengeNames[0], GetChallengeTitle(5));
		strcpy(challengeNames[1], GetChallengeTitle(18));
		strcpy(challengeNames[2], GetChallengeTitle(12));
		strcpy(challengeNames[3], GetChallengeTitle(13));
		strcpy(challengeNames[4], GetChallengeTitle(22));
		strcpy(challengeNames[5], GetChallengeTitle(15));
		break;
	case 32:
		pmenuCur->menuheader.pszHeaderText = g_pszStartChallengeMenu_CheckPoint[g_GameSettings.m_cActiveLanguage];
		strcpy(challengeNames[0], GetChallengeTitle(6));
		strcpy(challengeNames[1], GetChallengeTitle(8));
		strcpy(challengeNames[2], GetChallengeTitle(3));
		strcpy(challengeNames[3], GetChallengeTitle(17));
		strcpy(challengeNames[4], GetChallengeTitle(11));
		break;
	default:
		pmenuCur->menuheader.pszHeaderText = g_pszStartSinglePlayerMenu_Challenge[g_GameSettings.m_cActiveLanguage];
		strcpy(challengeNames[0], g_pszStartChallengeMenu_Gauntlet[g_GameSettings.m_cActiveLanguage]);
		strcpy(challengeNames[1], g_pszStartChallengeMenu_Timed[g_GameSettings.m_cActiveLanguage]);
		strcpy(challengeNames[2], g_pszStartChallengeMenu_Goal[g_GameSettings.m_cActiveLanguage]);
		strcpy(challengeNames[3], g_pszStartChallengeMenu_Flag[g_GameSettings.m_cActiveLanguage]);
		strcpy(challengeNames[4], g_pszStartChallengeMenu_CheckPoint[g_GameSettings.m_cActiveLanguage]);
		break;
	}

	return sRes;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called by the menu API when an item is chosen in menuChallenge.
//
////////////////////////////////////////////////////////////////////////////////
static bool ChallengeChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		 // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)		 // Item chosen.
{
	bool bAcceptChoice = true; // Assume accepting.

	// Audible Feedback.
	if (sMenuItem == -1)
	{
		g_sSelectedChallengeMode = -1;
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	}
	else
	{
		g_sSelectedChallengeMode = 0;
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);
	}

	// Let game module handle it
//	if (sMenuItem == 0)
//		Game_StartChallengeGame(0);

	return bAcceptChoice;
}

static bool ChallengeTimedChoice(
	Menu *pmenuCurrent,
	int16_t sMenuItem, int16_t sMenuCurr)
{
	//Assume Accepting
	bool bAcceptChoice = true;

	//Audible Feedback
	if (sMenuItem == -1)
	{
		g_sSelectedChallengeLevel = -1;
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	}
	else
	{
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);
	

	//HANDLE SELECTION
	switch (sMenuItem)
	{
	case 0:
		g_sSelectedChallengeLevel = 10;		
		break;
	case 1:
		g_sSelectedChallengeLevel = 4;
		break;
	case 2:
		g_sSelectedChallengeLevel = 20;
		break;
	case 3:
		g_sSelectedChallengeLevel = 7;
		break;
	case 4:
		g_sSelectedChallengeLevel = 9;
		break;
	default:
		break;
	}

	}
	return bAcceptChoice;
}

static bool ChallengeGoalChoice(
	Menu *pmenuCurrent,
	int16_t sMenuItem, int16_t sMenuCurr)
{
	//Assume Accepting
	bool bAcceptChoice = true;

	//Audible Feedback
	if (sMenuItem == -1)
	{
		g_sSelectedChallengeLevel = -1;
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	}
	else
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);
{
	//HANDLE SELECTION
	switch (sMenuItem)
	{
	case 0:
		g_sSelectedChallengeLevel = 16;
		break;
	case 1:
		g_sSelectedChallengeLevel = 14;
		break;
	case 2:
		g_sSelectedChallengeLevel = 2;
		break;
	case 3:
		g_sSelectedChallengeLevel = 19;
		break;
	case 4:
		g_sSelectedChallengeLevel = 23;
		break;
	case 5:
		g_sSelectedChallengeLevel = 21;
		break;
	default:
		break;
	}
}

	return bAcceptChoice;
}

static bool ChallengeFlagChoice(
	Menu *pmenuCurrent,
	int16_t sMenuItem, int16_t sMenuCurr)
{
	//Assume Accepting
	bool bAcceptChoice = true;

	//Audible Feedback
	if (sMenuItem == -1)
	{
		g_sSelectedChallengeLevel = -1;
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	}
	else
{
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);

	//HANDLE SELECTION
	switch (sMenuItem)
	{
	case 0:
		g_sSelectedChallengeLevel = 5;
		break;
	case 1:
		g_sSelectedChallengeLevel = 18;
		break;
	case 2:
		g_sSelectedChallengeLevel = 12;
		break;
	case 3:
		g_sSelectedChallengeLevel = 13;
		break;
	case 4:
		g_sSelectedChallengeLevel = 22;
		break;
	case 5:
		g_sSelectedChallengeLevel = 15;
		break;
	default:
		break;
	}
}

	return bAcceptChoice;
}

static bool ChallengeCheckpointChoice(
	Menu *pmenuCurrent,
	int16_t sMenuItem, int16_t sMenuCurr)
{
	//Assume Accepting
	bool bAcceptChoice = true;

	//Audible Feedback
	if (sMenuItem == -1)
	{
		g_sSelectedChallengeLevel = -1;
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	}
	else
	{
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);

	//HANDLE SELECTION
	switch (sMenuItem)
	{
	case 0:
		g_sSelectedChallengeLevel = 6;
		break;
	case 1:
		g_sSelectedChallengeLevel = 8;
		break;
	case 2:
		g_sSelectedChallengeLevel = 3;
		break;
	case 3:
		g_sSelectedChallengeLevel = 17;
		break;
	case 4:
		g_sSelectedChallengeLevel = 11;
		break;
	default:
		break;
	}
	}
	return bAcceptChoice;
}
////////////////////////////////////////////////////////////////////////////////
//
// Called to init or kill the Start Single Player Game menu.
//
////////////////////////////////////////////////////////////////////////////////
static int16_t StartSingleInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit)				// TRUE, if initializing; FALSE, if killing.
{		

	int8_t cSize = 0;
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 2:
		case 3:
			cSize = 1;
		break;
	}
	MenuResize(pmenuCur, cSize);

	pmenuCur->menuheader.pszHeaderText = g_pszStartGameMenu_GameStart[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[0].pszText = g_pszGameMenu_Continue[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[1].pszText = g_pszStartSinglePlayerMenu_NewCampaign[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[2].pszText = g_pszStartSinglePlayerMenu_LoadLevel[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[3].pszText = g_pszStartSinglePlayerMenu_Challenge[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[4].pszText = g_pszStartSinglePlayerMenu_Coop[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[5].pszText = g_pszStartSinglePlayerMenu_ChangeSave[g_GameSettings.m_cActiveLanguage];

	if (sInit)
	{		
		g_sActivePlayers = PLAYER_1;
		bool bNoVmu = true;
		bool bNoExtraControllers = true;
		if (MAPLE::cMapleVmu::m_pActiveVmu != nullptr && g_sSaveGameSlot >= 0 && g_SaveGameState[g_sSaveGameSlot].sRealmNum[0] > 0)
		{
			pmenuCur->ami[0].sEnabled = 1;
		}
		else
		{
			pmenuCur->ami[0].sEnabled = 0;
		}
	
		if (MAPLE::cMapleVmu::m_pActiveVmu != nullptr)
		{			
			bNoVmu = false;
		}
				
		for (int index = 0; index < MAPLE_CONTROLLER_MAX; index++)
		{
			MAPLE::cMapleController *pController = rspGetJoy(index);		
			if (index > 0 && pController->Status() != MAPLE::cMapleController::ControllerDisconnected)
			{
				bNoExtraControllers = false;
			}

			if (bNoVmu)
			{
				if (pController->m_pMemCards[0]->Status() == MAPLE::cMapleModule::Connected && 					
					(pController->m_pMemCards[0]->FileExists(MAPLE::cMapleVmu::m_pszSaveAppName) || 
					pController->m_pMemCards[0]->GetFreeBlocks() >= SAVE_FILE_BLOCKS))
				{			
					bNoVmu = false;				
				}
						
				if (pController->m_pMemCards[1]->Status() == MAPLE::cMapleModule::Connected && 					
					(pController->m_pMemCards[1]->FileExists(MAPLE::cMapleVmu::m_pszSaveAppName) || 
					pController->m_pMemCards[1]->GetFreeBlocks() >= SAVE_FILE_BLOCKS))
				{			
					bNoVmu = false;				
				}			
			}			
		}

		pmenuCur->ami[4].sEnabled = (!bNoExtraControllers) ? 1 : 0;	
		pmenuCur->ami[5].sEnabled = (!bNoVmu) ? 1 : 0;
	}
			
	return 0;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called by the menu API when an item is chosen in menuStartSingle.
//
////////////////////////////////////////////////////////////////////////////////
static bool StartSingleMenu( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		 // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)		 // Item chosen.
{
	bool bAcceptChoice = true; // Assume accepting.

	// Audible Feedback.
	if (sMenuItem == -1)
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	else
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);

	// Let game module handle it... Sorta.
	// Because of the new menu split the numbers don't match up.
	// Let's figure that out.
	switch (sMenuItem)	
	{
	case 0: // Load game
		Game_StartSinglePlayerGame(4);
	break;	
	case 5: // Change save slot
		int8_t cVmuCount = MAPLE::cMapleVmu::GetCount();
		if (cVmuCount)
		{
			for (int index = 0; index < MAPLE_CONTROLLER_MAX; index++)
			{
				MAPLE::cMapleController *pController = rspGetJoy(index);
				if (pController && pController->Status() != MAPLE::cMapleController::ControllerDisconnected)
				{
					// Refresh memory card statuses
					pController->m_pMemCards[0]->Status();
					pController->m_pMemCards[1]->Status();
				}
			}
		}	
		break;
	}

	return bAcceptChoice;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called to init or kill the New Campaign menu.
//
////////////////////////////////////////////////////////////////////////////////
static int16_t NewCampaignInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit)				// TRUE, if initializing; FALSE, if killing.
{			
	g_sSelectedChallengeMode = -1;
	g_sSelectedChallengeLevel = -1;	
	g_sSelectedSingleLevel = -1;	
	pmenuCur->menuheader.pszHeaderText = g_pszStartSinglePlayerMenu_NewCampaign[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[0].pszText = g_pszStartSinglePlayerMenu_New[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[1].pszText = g_pszStartSinglePlayerMenu_AddOn[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[2].pszText = g_pszStartSinglePlayerMenu_AddOn2[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[3].pszText = g_pszStartSinglePlayerMenu_AllLevels[g_GameSettings.m_cActiveLanguage];

	return 0;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called by the menu API when an item is chosen in menuNewCampaign.
//
////////////////////////////////////////////////////////////////////////////////
static bool NewCampaignMenu( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		 // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)		 // Item chosen.
{
	bool bAcceptChoice = true; // Assume accepting.

	// Audible Feedback.
	if (sMenuItem == -1)
	{
		g_sSelectedGameMode = -1;
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	}		
	else
	{
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);
		g_sSelectedGameMode = sMenuItem;
	}
						
	return bAcceptChoice;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called to init or kill the New Campaign coop menu.
//
////////////////////////////////////////////////////////////////////////////////
static int16_t NewCampaignCoopInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit)				// TRUE, if initializing; FALSE, if killing.
{
	pmenuCur->menuheader.pszHeaderText = g_pszStartSinglePlayerMenu_Coop[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[0].pszText = g_pszGameMenu_Continue[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[1].pszText = g_pszStartSinglePlayerMenu_New[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[2].pszText = g_pszStartSinglePlayerMenu_AddOn[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[3].pszText = g_pszStartSinglePlayerMenu_AddOn2[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[4].pszText = g_pszStartSinglePlayerMenu_AllLevels[g_GameSettings.m_cActiveLanguage];

	if (MAPLE::cMapleVmu::m_pActiveVmu != nullptr && g_sSaveGameSlot >= 0 && g_SaveGameState[g_sSaveGameSlot].sRealmNum[1] > 0)
	{
		pmenuCur->ami[0].sEnabled = 1;
	}
	else
	{
		pmenuCur->ami[0].sEnabled = 0;
	}
	
	return 0;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called by the menu API when an item is chosen in menuNewCampaignCoop.
//
////////////////////////////////////////////////////////////////////////////////
static bool NewCampaignCoopMenu( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		 // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)		 // Item chosen.
{
	bool bAcceptChoice = true; // Assume accepting.

	// Audible Feedback.
	if (sMenuItem == -1)
	{
		g_sSelectedGameMode = -1;
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	}		
	else if (sMenuItem == 0)
	{		
		Game_StartSinglePlayerGame(5);	
	}
	else
	{
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);
		g_sSelectedGameMode = sMenuItem - 1;
	}
						
	return bAcceptChoice;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called to init or kill the options menu.
//
////////////////////////////////////////////////////////////////////////////////
static int16_t OptionsInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			// Current menu.
	int16_t sInit)			// TRUE, if initializing; FALSE, if killing.
{	
	pmenuCur->menuheader.pszHeaderText = g_pszMainMenu_Options[g_GameSettings.m_cActiveLanguage];	
	pmenuCur->ami[0].pszText = g_pszOptionsMenu_Audio[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[1].pszText = g_pszOptionsMenu_Controls[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[2].pszText = g_pszGameMenu_Title[g_GameSettings.m_cActiveLanguage];

	return 0;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called when a choice is made or changed on the options menu.
//
////////////////////////////////////////////////////////////////////////////////
static bool OptionsChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,	   // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)	   // Item chosen or -1 for change of focus.
{
	if (sMenuItem == -1)
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	else
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);
	return true;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called to init or kill the game options menu.
//
////////////////////////////////////////////////////////////////////////////////
static int16_t GameOptionsInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit)				// TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.

	pmenuCur->menuheader.pszHeaderText = g_pszGameMenu_Title[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[0].pszText = g_pszMultiplayerSetupMenu_Color[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[1].pszText = g_pszInterface[g_GameSettings.m_cActiveLanguage];	
	pmenuCur->ami[2].pszText = g_pszLanguage[g_GameSettings.m_cActiveLanguage];
	
	if (g_GameSettings.m_cActiveLanguage == 1)
	{
		pmenuCur->menupos.sX = 0;
		pmenuCur->menupos.sY = 0;
		pmenuCur->menupos.sW = 640;
		pmenuCur->menupos.sH = 480;
		pmenuCur->menupos.sHeaderX = -60;
		pmenuCur->menupos.sHeaderY = MENU_HEAD_Y_LG;
		pmenuCur->menupos.sItemX = MENU_ITEM_X_LG;
		pmenuCur->menupos.sItemY = MENU_ITEM_Y_LG;
		pmenuCur->menupos.sItemSpacingY = MENU_ITEM_SPACE_Y_LG;
		pmenuCur->menupos.sIndicatorSpacingX = MENU_ITEM_IND_SPACE_X_LG;
	}
	else
	{		
		pmenuCur->menupos.sX = 20;
		pmenuCur->menupos.sY = 20;
		pmenuCur->menupos.sW = 450;
		pmenuCur->menupos.sH = 460;
		pmenuCur->menupos.sHeaderX = -60;
		pmenuCur->menupos.sHeaderY = MENU_HEAD_Y_MD;
		pmenuCur->menupos.sItemX = MENU_ITEM_X_MD;
		pmenuCur->menupos.sItemY = MENU_ITEM_Y_MD;
		pmenuCur->menupos.sItemSpacingY = MENU_ITEM_SPACE_Y_MD;
		pmenuCur->menupos.sIndicatorSpacingX = MENU_ITEM_IND_SPACE_X_MD;
	}

	if (sInit != FALSE)
	{
		int16_t sMenuItem = 0;	
		if (rspGetResourceInstance(&g_resmgrShell, PLAYER_COLOR_GUI_FILE, &ms_ptxtColor) == 0)
		{
			// Keep in bounds just in case (anyone could type any number into the INI) . . .
			if (g_GameSettings.m_sPlayerColorIndex >= CGameSettings::ms_sNumPlayerColorDescriptions || g_GameSettings.m_sPlayerColorIndex >= CDude::MaxTextures || g_GameSettings.m_sPlayerColorIndex < 0)
			{
				g_GameSettings.m_sPlayerColorIndex = 0;
			}
			// Set the text from the INI setting. Note that we are changing a
			// resource!
			// todo magic numbers
			ms_ptxtColor->widthPvr = 175;
			ms_ptxtColor->heightPvr = 23;		
			ms_ptxtColor->tex = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_MENU_TEXT_BG);
			ms_ptxtColor->m_sOffsetX = 3;
			ms_ptxtColor->m_sOffsetY = 2;
			ms_ptxtColor->SetText("%s", CGameSettings::ms_apszPlayerColorDescriptions[g_GameSettings.m_sPlayerColorIndex][g_GameSettings.m_cActiveLanguage]);
			ms_ptxtColor->m_pprint->SetFont(19, &g_fontBig);
			ms_ptxtColor->Compose();
			ms_ptxtColor->isPvr = true;
			pmenuCur->ami[sMenuItem++].pgui = ms_ptxtColor;
		}
		else
		{		
			sRes = 2;
		}
		
		if (rspGetResourceInstance(&g_resmgrShell, PLAYER_COLOR_GUI_FILE, &ms_ptxtInterface) == 0)
		{		
			ms_ptxtInterface->isPvr = true;			
			// todo magic numbers
			ms_ptxtInterface->widthPvr = 175;
			ms_ptxtInterface->heightPvr = 23;
			ms_ptxtInterface->m_sOffsetX = 3;
			ms_ptxtInterface->m_sOffsetY = 2;					
			ms_ptxtInterface->tex = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_MENU_TEXT_BG);
			// Set the text from the INI setting.			
			ms_ptxtInterface->SetText("%s", g_pszInterfaces[g_GameSettings.m_cHudMode][g_GameSettings.m_cActiveLanguage]);
			ms_ptxtInterface->m_pprint->SetFont(19, &g_fontBig);
			ms_ptxtInterface->Compose();						
			pmenuCur->ami[sMenuItem++].pgui = ms_ptxtInterface;
		}
		else
		{
			TRACE("GameOptionsInit(): rspGetResource() failed.\n");
			sRes = 2;
		}

		if (rspGetResourceInstance(&g_resmgrShell, PLAYER_COLOR_GUI_FILE, &ms_ptxtLanguage) == 0)
		{		
			ms_ptxtLanguage->isPvr = true;			
			// todo magic numbers
			ms_ptxtLanguage->widthPvr = 175;
			ms_ptxtLanguage->heightPvr = 23;
			ms_ptxtLanguage->m_sOffsetX = 3;
			ms_ptxtLanguage->m_sOffsetY = 2;					
			ms_ptxtLanguage->tex = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_MENU_TEXT_BG);				
			ms_ptxtLanguage->SetText("%s", g_pszLanguages[g_GameSettings.m_cActiveLanguage]);
			ms_ptxtLanguage->m_pprint->SetFont(19, &g_fontBig);
			ms_ptxtLanguage->Compose();						
			pmenuCur->ami[sMenuItem++].pgui = ms_ptxtLanguage;
		}
		else
		{
			TRACE("GameOptionsInit(): rspGetResource() failed.\n");
			sRes = 2;
		}	


		if (rspGetResourceInstance(&g_resmgrShell, PLAYER_COLOR_GUI_FILE, &ms_ptxtUnderscan) == 0)
		{		
			ms_ptxtUnderscan->isPvr = true;			
			// todo magic numbers
			ms_ptxtUnderscan->widthPvr = 175;
			ms_ptxtUnderscan->heightPvr = 23;
			ms_ptxtUnderscan->m_sOffsetX = 3;
			ms_ptxtUnderscan->m_sOffsetY = 2;					
			ms_ptxtUnderscan->tex = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_MENU_TEXT_BG);												
			ms_ptxtUnderscan->SetText("%s", g_GameSettings.m_cCutSceneUnderscan ? "Cutscene" :  g_psz_Off[g_GameSettings.m_cActiveLanguage]);
			ms_ptxtUnderscan->m_pprint->SetFont(19, &g_fontBig);
			ms_ptxtUnderscan->Compose();						
			pmenuCur->ami[sMenuItem].pgui = ms_ptxtUnderscan;
			if (rspIsVGA())
			{
				pmenuCur->ami[sMenuItem].sEnabled = false;
			}
			sMenuItem++;
		}
		else
		{
			TRACE("GameOptionsInit(): rspGetResource() failed.\n");
			sRes = 2;
		}
	}
	else
	{
		int16_t sMenuItem = 0;
		if (ms_ptxtColor != NULL)
		{
			// Release resource.			
			rspReleaseResourceInstance(&g_resmgrShell, &ms_ptxtColor);
			// Clear menu's pointer.
			TRACE("Before ms_ptxtColor assignment. sMenuItem = %d\n", sMenuItem);
			pmenuCur->ami[sMenuItem++].pgui = NULL;
			TRACE("After ms_ptxtColor assignment. sMenuItem = %d\n", sMenuItem);
		}

		if (ms_ptxtInterface != NULL)
		{
			// Release resource.
			rspReleaseResourceInstance(&g_resmgrShell, &ms_ptxtInterface);

			// Clear menu's pointer.
			pmenuCur->ami[sMenuItem++].pgui = NULL;
		}

		if (ms_ptxtLanguage != NULL)
		{
			// Release resource.
			rspReleaseResourceInstance(&g_resmgrShell, &ms_ptxtLanguage);

			// Clear menu's pointer.
			pmenuCur->ami[sMenuItem++].pgui = NULL;
		}

		if (ms_ptxtUnderscan != NULL)
		{
			// Release resource.
			rspReleaseResourceInstance(&g_resmgrShell, &ms_ptxtUnderscan);

			// Clear menu's pointer.
			pmenuCur->ami[sMenuItem++].pgui = NULL;
		}

		if (g_GameSettings.m_sPlayerColorIndex != g_SaveGameState[g_sSaveGameSlot].sPlayerColorIndex ||
		g_GameSettings.m_cHudMode != g_SaveGameState[g_sSaveGameSlot].cHudMode)
		{		
			switch (g_GameSettings.m_cHudMode)
			{
				case 0:						
					g_GameSettings.m_sViewH = 400;
					g_GameSettings.m_sFilmY = 40;
					break;
				case 1:
					g_GameSettings.m_sViewH = 480;			
					g_GameSettings.m_sFilmY = 0;
				break;
			}

			g_pvrRenderer->SetClip(0, g_GameSettings.m_sFilmY, 640, g_GameSettings.m_sViewH);

			if (MAPLE::cMapleVmu::m_pActiveVmu != nullptr && MAPLE::cMapleVmu::m_pActiveVmu->Status() == MAPLE::cMapleModule::Connected)
			{			
				Game_SavePlayersPrefs(MAPLE::cMapleVmu::m_pActiveVmu, g_sSaveGameSlot, MAPLE::cMapleVmu::m_pszSaveAppName);
			}
		}		
	}
	return sRes;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called when a choice is made or changed on the game options menu.
//
////////////////////////////////////////////////////////////////////////////////
static bool GameOptionsChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		   // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)		   // Item chosen or -1 for change of focus.
{
	bool bAcceptChoice = true; // Assume accepting.

	switch (sMenuItem)
	{
	case 0:
		// Increment and check to make sure we have a description and we have such a color . . .
		g_GameSettings.m_sPlayerColorIndex++;
		if (g_GameSettings.m_sPlayerColorIndex >= CGameSettings::ms_sNumPlayerColorDescriptions || g_GameSettings.m_sPlayerColorIndex >= CDude::MaxTextures)
		{
			g_GameSettings.m_sPlayerColorIndex = 0;
		}

		// Set the text from the INI setting. Note that we are changing a
		// resource!
		ms_ptxtColor->SetText("%s", CGameSettings::ms_apszPlayerColorDescriptions[g_GameSettings.m_sPlayerColorIndex][g_GameSettings.m_cActiveLanguage]);
		ms_ptxtColor->Compose();
		break;

	case 1:
		// Increment and check to make sure that we have such a language
		g_GameSettings.m_cHudMode++;

		if (g_GameSettings.m_cHudMode >= 2 || g_GameSettings.m_cHudMode < 0)
		{
			g_GameSettings.m_cHudMode = 0;
		}

		switch (g_GameSettings.m_cHudMode)
		{
			case 0:						
				g_GameSettings.m_sViewH = 400;
				g_GameSettings.m_sFilmY = 40;
				break;
			case 1:
				g_GameSettings.m_sViewH = 480;			
				g_GameSettings.m_sFilmY = 0;
			break;
		}

		g_pvrRenderer->SetClip(0, g_GameSettings.m_sFilmY, 640, g_GameSettings.m_sViewH);

		// Set the text from the INI setting.
		ms_ptxtInterface->SetText("%s", g_pszInterfaces[g_GameSettings.m_cHudMode][g_GameSettings.m_cActiveLanguage]);
		ms_ptxtInterface->Compose();
		break;

	case 2:		
		// Increment and check to make sure that we have such a language
		g_GameSettings.m_cActiveLanguage++;
		if (g_GameSettings.m_cActiveLanguage >= NUM_LANGUAGES || g_GameSettings.m_cActiveLanguage < 0)
		{
			g_GameSettings.m_cActiveLanguage = 0;
		}

		// Set the text from the INI setting.
		ms_ptxtLanguage->SetText("%s", g_pszLanguages[g_GameSettings.m_cActiveLanguage]);
		ms_ptxtLanguage->Compose();
		break;	

	case 3:		
		if (g_GameSettings.m_cCutSceneUnderscan)
		{			
			g_GameSettings.m_cCutSceneUnderscan = false;
		}
		else
		{
			g_GameSettings.m_cCutSceneUnderscan = true;
		}
				
		ms_ptxtUnderscan->SetText("%s", g_GameSettings.m_cCutSceneUnderscan ? "Cutscene" :  g_psz_Off[g_GameSettings.m_cActiveLanguage]);
		ms_ptxtUnderscan->Compose();
		break;
	}
	
	// Audible Feedback.
	if (sMenuItem == -1)
	{
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);			
	}		
	else
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);


	if (sMenuItem == 4)
	{
		if (g_GameSettings.m_sPlayerColorIndex != g_SaveGameState[g_sSaveGameSlot].sPlayerColorIndex || 
			g_GameSettings.m_cHudMode != g_SaveGameState[g_sSaveGameSlot].cHudMode)
		{
			if (g_GameSettings.m_cHudMode != g_SaveGameState[g_sSaveGameSlot].cHudMode)
			{
				g_bResetCamera = true;
			}

			if (g_pvrRenderer->m_szDrawMode == PVR::cRenderMgr::Batched && MAPLE::cMapleVmu::m_pActiveVmu != nullptr && MAPLE::cMapleVmu::m_pActiveVmu->Status() == MAPLE::cMapleModule::Connected)
			{			
				float r = g_pvrRenderer->m_fGlobalRScale;
				float g = g_pvrRenderer->m_fGlobalGScale;
				float b = g_pvrRenderer->m_fGlobalBScale;
				g_pvrRenderer->DrawQuad(g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_SPLASH_THIRD), 0, 0, 640, 480, PVR::cRenderMgr::LayerBg);

				g_pvrRenderer->m_fGlobalRScale = 1.f;
				g_pvrRenderer->m_fGlobalGScale = 1.f;
				g_pvrRenderer->m_fGlobalBScale = 1.f;

				RPrint print;								
				print.SetFont(30, &g_fontPostal);
				print.SetColorRGB(192, 0, 0);
				print.SetEffectAbs(RPrint::SHADOW_X, 1);
				print.SetEffectAbs(RPrint::SHADOW_Y, 1);
				print.SetWordWrap(TRUE);

				int16_t sOffsetX = 0;
				switch (g_GameSettings.m_cActiveLanguage)
				{
					case 0:
						sOffsetX = 260;
					break;
					case 1:
						sOffsetX = 235;
					break;
					case 2:
						sOffsetX = 155;
					break;
					case 3:
						sOffsetX = 175;
					break;
					case 4:
						sOffsetX = 175;
					break;
					case 5:
						sOffsetX = 215;
					break;
				}
				print.SetColumn(sOffsetX, (g_GameSettings.m_cCutSceneUnderscan ? 390 : 420), 410, 50);
				print.printPVR(0, 0, g_pszSaving[g_GameSettings.m_cActiveLanguage]);	
				
				DoMenuOutput();
				g_pvrRenderer->DrawGuiText();
				g_pvrRenderer->RenderBatchedFrame();
				g_pvrRenderer->FlushBatchedDrawQueues();
				g_pvrRenderer->m_pGuiTextString->Flush();

				g_pvrRenderer->m_fGlobalRScale = r;
				g_pvrRenderer->m_fGlobalGScale = g;
				g_pvrRenderer->m_fGlobalBScale = b;
			}				
		}		
	}

	return bAcceptChoice;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called to init or kill the play options menu.
//
////////////////////////////////////////////////////////////////////////////////
static int16_t PlayOptionsInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit)				// TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.

	pmenuCur->menuheader.pszHeaderText = g_pszDifficultyMenu_Title[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[0].pszText = g_pszGameOptionsMenu_Easy[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[1].pszText = g_pszGameOptionsMenu_Medium[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[2].pszText = g_pszGameOptionsMenu_Hard[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[3].pszText = g_pszGameOptionsMenu_Nightmare[g_GameSettings.m_cActiveLanguage];

	return sRes;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called when a choice is made or changed on the play options menu.
//
////////////////////////////////////////////////////////////////////////////////
static bool PlayOptionsChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		   // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)		   // Item chosen or -1 for change of focus.
{
	bool bAcceptChoice = true; // Assume accepting.

	// Audible Feedback.
	if (sMenuItem == -1)
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	else
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);
	
	// Let game module handle it	
	Menu *currentMenu = GetCurrentMenu();

	g_GameSettings.m_sDifficulty = 0;

	switch (sMenuItem)
	{
		case 0:		
		g_GameSettings.m_sDifficulty = 1;		
		break;
		case 1:
		g_GameSettings.m_sDifficulty = 4;		
		break;
		case 2:
		g_GameSettings.m_sDifficulty = 7;		
		break;
		case 3:
		g_GameSettings.m_sDifficulty = 11;		
		break;
	}

	if (g_GameSettings.m_sDifficulty)
	{
		if(g_sSelectedSingleLevel >= 0)				
			Game_StartLevelOnce(g_sSelectedSingleLevel);
		
		if (g_sSelectedGameMode >= 0)		
			Game_StartSinglePlayerGame(g_sSelectedGameMode);

		if (g_sSelectedChallengeMode >= 0)
			Game_StartChallengeGame(g_sSelectedChallengeMode);

		if (g_sSelectedChallengeLevel >= 0)
			Game_StartChallengeGame(g_sSelectedChallengeLevel);
	}

	return bAcceptChoice;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called to init or kill the audio options menu.
//
////////////////////////////////////////////////////////////////////////////////
static int16_t AudioOptionsInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				 // Current menu.
	int16_t sInit)				 // TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.			
	int8_t cSize = 0;
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 1:			
		case 3:	
		case 4:			
		case 5:
			cSize = 1;			
		break;		
	}
	MenuResize(pmenuCur, cSize);	
	
	pmenuCur->menuheader.pszHeaderText = g_pszAudioMenu_Title[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[0].pszText = g_pszAudioMenu_Mixer[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[1].pszText = g_pszAudioMenu_Language[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[2].pszText = g_pszAudioMenu_PainFrequency[g_GameSettings.m_cActiveLanguage];

	if (sInit != FALSE)
	{
		if (rspGetResource(&g_resmgrShell, PLAYER_COLOR_GUI_FILE, &ms_ptxtLanguageAudio) == 0)
		{
			// Keep in bounds just in case (anyone could type any number into the INI)
			if (g_GameSettings.m_sAudioLanguage >= NUM_AUDIO_LANGUAGES || g_GameSettings.m_sAudioLanguage < 0)
			{
#if LOCALE == JAPAN
				g_GameSettings.m_sAudioLanguage = JAPANESE_AUDIO;
#else
				g_GameSettings.m_sAudioLanguage = ENGLISH_AUDIO;
#endif
			}
			ms_ptxtLanguageAudio->isPvr = true;						
			// todo magic numbers
			ms_ptxtLanguageAudio->widthPvr = 175;
			ms_ptxtLanguageAudio->heightPvr = 23;
			ms_ptxtLanguageAudio->m_sOffsetX = 3;
			ms_ptxtLanguageAudio->m_sOffsetY = 2;					
			ms_ptxtLanguageAudio->tex = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_MENU_TEXT_BG);
			// Set the text from the INI setting.			
			ms_ptxtLanguageAudio->SetText("%s", CGameSettings::ms_apszAudioLanguageDescriptions[g_GameSettings.m_sAudioLanguage][g_GameSettings.m_cActiveLanguage]);
			ms_ptxtLanguageAudio->m_pprint->SetFont(19, &g_fontBig);
			ms_ptxtLanguageAudio->Compose();						
			pmenuCur->ami[1].pgui = ms_ptxtLanguageAudio;
		}
		else
		{
			TRACE("AudioOptionsInit(): rspGetResource() failed.\n");
			sRes = 2;
		}

		if (rspGetResource(&g_resmgrShell, GUI_VOLUME_FILE, (RScrollBar **)&(pmenuCur->ami[2].pgui)) == 0)
		{
			RScrollBar *psb = (RScrollBar *)(pmenuCur->ami[2].pgui);
			int32_t lMin, lMax, lRange;
			psb->GetRange(&lMin, &lMax);
			lRange = lMax - lMin;
			psb->m_lButtonIncDec = lRange / 14; // using 16 here results in 18 stop points?!
			psb->m_lTrayIncDec = psb->m_lButtonIncDec;
			int8_t pos = g_GameSettings.m_sPainFrequency;
			// work around weird range issue halfway through slider
			if (pos < 10)
				pos--;
			psb->SetPos((pos * lRange / 16));
			psb->m_upcUser = PainFrequencyScrollUpdate;
			RGuiItem *pguiVal = psb->GetItemFromId(GUI_ID_VOLUME_VAL);
			if (pguiVal)
			{
				pguiVal->SetText("%s", " ");
				pguiVal->Compose();
			}
			else
			{
				TRACE("AudioOptionsInit(): Could not get at pguiVal\n");
			}
		}
		else
		{
			TRACE("AudioOptionsInit(): Failed to initialise pain frequency slider.\n");
			sRes = 2;
		}
		
	}
	else
	{
		if (ms_ptxtLanguageAudio != NULL)
		{
			// Release resource.
			rspReleaseResource(&g_resmgrShell, &ms_ptxtLanguageAudio);

			// Clear menu's pointer.
			pmenuCur->ami[2].pgui = NULL;
		}

		if (pmenuCur->ami[3].pgui)
		{
			rspReleaseResource(&g_resmgrShell, &(pmenuCur->ami[3].pgui));
			pmenuCur->ami[3].pgui = NULL;
		}

		if (g_GameSettings.m_sAudioLanguage != g_SaveGameState[g_sSaveGameSlot].sAudioLanguage 
		|| g_GameSettings.m_sPainFrequency != g_SaveGameState[g_sSaveGameSlot].sPainFrequency)
		{			
			if (MAPLE::cMapleVmu::m_pActiveVmu != nullptr && MAPLE::cMapleVmu::m_pActiveVmu->Status() == MAPLE::cMapleModule::Connected)
			{			
				Game_SavePlayersPrefs(MAPLE::cMapleVmu::m_pActiveVmu, g_sSaveGameSlot, MAPLE::cMapleVmu::m_pszSaveAppName);
			}
		}		
	}

	return sRes;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called when a choice is made or changed on the audio options menu.
//
////////////////////////////////////////////////////////////////////////////////
static bool AudioOptionsChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,			// Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)			// Item chosen or -1 for change of focus.
{
	bool bAcceptChoice = true; // Assume accepting.

	// Audible Feedback.
	if (sMenuItem == -1)	
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	else
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);

	if (sMenuItem == 0 || sMenuItem == 3)
	{
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
			if (g_GameSettings.m_sAudioLanguage != g_SaveGameState[g_sSaveGameSlot].sAudioLanguage 
		|| g_GameSettings.m_sPainFrequency != g_SaveGameState[g_sSaveGameSlot].sPainFrequency)
		{
			if (g_pvrRenderer->m_szDrawMode == PVR::cRenderMgr::Batched && MAPLE::cMapleVmu::m_pActiveVmu != nullptr && MAPLE::cMapleVmu::m_pActiveVmu->Status() == MAPLE::cMapleModule::Connected)
			{
				float r = g_pvrRenderer->m_fGlobalRScale;
				float g = g_pvrRenderer->m_fGlobalGScale;
				float b = g_pvrRenderer->m_fGlobalBScale;
				g_pvrRenderer->DrawQuad(g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_SPLASH_THIRD), 0, 0, 640, 480, PVR::cRenderMgr::LayerBg);

				g_pvrRenderer->m_fGlobalRScale = 1.f;
				g_pvrRenderer->m_fGlobalGScale = 1.f;
				g_pvrRenderer->m_fGlobalBScale = 1.f;

				RPrint print;								
				print.SetFont(30, &g_fontPostal);
				print.SetColorRGB(192, 0, 0);
				print.SetEffectAbs(RPrint::SHADOW_X, 1);
				print.SetEffectAbs(RPrint::SHADOW_Y, 1);
				print.SetWordWrap(TRUE);
				int16_t sOffsetX = 0;
				switch (g_GameSettings.m_cActiveLanguage)
				{
					case 0:
						sOffsetX = 260;
					break;
					case 1:
						sOffsetX = 235;
					break;
					case 2:
						sOffsetX = 155;
					break;
					case 3:
						sOffsetX = 175;
					break;
					case 4:
						sOffsetX = 175;
					break;
					case 5:
						sOffsetX = 215;
					break;
				}
				print.SetColumn(sOffsetX, (g_GameSettings.m_cCutSceneUnderscan ? 390 : 420), 410, 50);
				print.printPVR(0, 0, g_pszSaving[g_GameSettings.m_cActiveLanguage]);
				g_pvrRenderer->DrawGuiText();				
				DoMenuOutput();
				g_pvrRenderer->RenderBatchedFrame();
				g_pvrRenderer->FlushBatchedDrawQueues();
				g_pvrRenderer->m_pGuiTextString->Flush();

				g_pvrRenderer->m_fGlobalRScale = r;
				g_pvrRenderer->m_fGlobalGScale = g;
				g_pvrRenderer->m_fGlobalBScale = b;
			}			
		}
	}

	if (sMenuItem == 1)
	{
		// Increment and check to make sure that we have such a language
		g_GameSettings.m_sAudioLanguage++;
		if (g_GameSettings.m_sAudioLanguage >= NUM_AUDIO_LANGUAGES || g_GameSettings.m_sAudioLanguage < 0)
		{
			g_GameSettings.m_sAudioLanguage = 0;
		}

		// Set the text from the INI setting.
		ms_ptxtLanguageAudio->SetText("%s", CGameSettings::ms_apszAudioLanguageDescriptions[g_GameSettings.m_sAudioLanguage][g_GameSettings.m_cActiveLanguage]);
		ms_ptxtLanguageAudio->Compose();
	}

	Game_AudioOptionsChoice(sMenuItem);

	return bAcceptChoice;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called to init or kill the volumes menu.
//
////////////////////////////////////////////////////////////////////////////////
static int16_t VolumesInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			// Current menu.
	int16_t sInit)			// TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.	
	int8_t cSize = 0;

	switch (g_GameSettings.m_cActiveLanguage)
	{			
		case 1:		
		case 3:			
		case 4:
		case 5:
			cSize = 1;			
		break;		
	}
	MenuResize(pmenuCur, cSize);	
		
	if (cSize)
	{
		pmenuCur->menupos.sHeaderY = MENU_HEAD_Y_MD;
		pmenuCur->menupos.sItemX = MENU_ITEM_X_MD;
		pmenuCur->menupos.sItemY = MENU_ITEM_Y_MD;
		pmenuCur->menupos.sItemSpacingY = MENU_ITEM_SPACE_Y_MD;
		pmenuCur->menupos.sIndicatorSpacingX = MENU_ITEM_IND_SPACE_X_MD;
	}

	pmenuCur->menuheader.pszHeaderText = g_pszVolumesMenu_Title[g_GameSettings.m_cActiveLanguage];

	if (sInit != FALSE)
	{
		RGuiItem::ms_print.SetFont(DEFAULT_GUI_FONT_HEIGHT, &g_fontPostal);

		int16_t i;
		for (i = 0; i < SampleMaster::MAX_NUM_SOUND_CATEGORIES && sRes == 0 && i < (NUM_ELEMENTS(pmenuCur->ami) - 2); i++)
		{
			if (rspGetResourceInstance(&g_resmgrShell, GUI_VOLUME_FILE, (RScrollBar **)&(pmenuCur->ami[i].pgui)) == 0)
			{
				RScrollBar *psb = (RScrollBar *)(pmenuCur->ami[i].pgui);
				ASSERT(psb->m_type == RGuiItem::ScrollBar);

				// Let the callback know which item this is.
				psb->m_ulUserData = i;
				int32_t lMin, lMax, lRange;
				psb->GetRange(&lMin, &lMax);
				lRange = lMax - lMin;
				// Set the initial position.
				psb->SetPos(g_GameSettings.m_asCategoryVolumes[i] * lRange / SampleMaster::UserMaxVolume);
				// Set the rate so based on the range.
				psb->m_lButtonIncDec = lRange / SampleMaster::UserMaxVolume;
				psb->m_lTrayIncDec = psb->m_lButtonIncDec;

				// Set the update call.  Note that we set the callback after setting the
				// initial position so we don't get a callback for that.
				psb->m_upcUser = VolumesScrollUpdate;

				// Note that we have to update the GUI val here b/c we don't allow the callback
				// until after we set the initial position of the scrollbar to avoid it playing
				// the example sample during that position change.

				// Get val indicator.
				RGuiItem *pguiVal = psb->GetItemFromId(GUI_ID_VOLUME_VAL);
				if (pguiVal)
				{
					pguiVal->SetText("%d", g_GameSettings.m_asCategoryVolumes[i]);
					pguiVal->Compose();
				}

				// Text.
				pmenuCur->ami[i].pszText = g_apszSoundCategories[i][g_GameSettings.m_cActiveLanguage];
			}
			else
			{
				TRACE("VolumesInit():  Failed to get resource.\n");
				sRes = 1;
			}
		}

		// Make the second to last one defaults.
		pmenuCur->ami[i].sEnabled = TRUE;
		pmenuCur->ami[i++].pszText = g_pszRotationSetupMenu_RestoreDefaults[g_GameSettings.m_cActiveLanguage];
		// Make the last one back.
#if 0
		static char szBack[]						= "";
		pmenuCur->ami[i].pszText				= szBack;
		pmenuCur->ami[i].sEnabled				= FALSE;
#endif
		pmenuCur->menuautoitems.sCancelItem = i;
	}
	else
	{
		int16_t i;
		for (i = 0; i < SampleMaster::MAX_NUM_SOUND_CATEGORIES && sRes == 0 && pmenuCur->ami[i].pszText; i++)
		{
			// If this resource was allocated . . .
			if (pmenuCur->ami[i].pgui)
			{
				// Release resource.
				rspReleaseResourceInstance(&g_resmgrShell, &(pmenuCur->ami[i].pgui));
			}
		}

		if (			
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[0] != g_GameSettings.m_asCategoryVolumes[0] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[1] != g_GameSettings.m_asCategoryVolumes[1] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[2] != g_GameSettings.m_asCategoryVolumes[2] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[3] != g_GameSettings.m_asCategoryVolumes[3] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[4] != g_GameSettings.m_asCategoryVolumes[4] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[5] != g_GameSettings.m_asCategoryVolumes[5] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[6] != g_GameSettings.m_asCategoryVolumes[6] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[7] != g_GameSettings.m_asCategoryVolumes[7] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[8] != g_GameSettings.m_asCategoryVolumes[8] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[9] != g_GameSettings.m_asCategoryVolumes[9] 
		)
		{
			if (MAPLE::cMapleVmu::m_pActiveVmu != nullptr && MAPLE::cMapleVmu::m_pActiveVmu->Status() == MAPLE::cMapleModule::Connected)
			{
				Game_SavePlayersPrefs(MAPLE::cMapleVmu::m_pActiveVmu, g_sSaveGameSlot, MAPLE::cMapleVmu::m_pszSaveAppName);
			}
		}
	}

	return sRes;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called when a choice is made or changed on the volumes menu.
//
////////////////////////////////////////////////////////////////////////////////
static bool VolumesChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,	   // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)	   // Item chosen or -1 for change of focus.
{
	bool bAcceptChoice = true; // Assume accepting.

	// Audible Feedback.
	switch (sMenuItem)
	{
	case -1: // Selection change.
		break;
	case SampleMaster::MAX_NUM_SOUND_CATEGORIES + 1: // Back.
		break;

	case SampleMaster::MAX_NUM_SOUND_CATEGORIES: // Restore defaults.
	{
		int16_t i;
		for (i = 0; i < SampleMaster::MAX_NUM_SOUND_CATEGORIES && i < NUM_ELEMENTS(pmenuCurrent->ami); i++)
		{
			if (pmenuCurrent->ami[i].pgui)
			{
				int32_t lMin, lMax, lRange;
				((RScrollBar *)(pmenuCurrent->ami[i].pgui))->GetRange(&lMin, &lMax);
				lRange = lMax - lMin;

				((RScrollBar *)(pmenuCurrent->ami[i].pgui))->SetPos(SampleMaster::UserDefaultVolume * lRange / SampleMaster::UserMaxVolume);

				// This'll keep the next sample from aborting the current so we can let the user
				// hear all the sounds when 'defaults' is chosen.
				ms_siLastSamplePlayed = 0;
			}
		}
		break;
	}	
	}

	if (sMenuItem == 11)
	{		
		if (			
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[0] != g_GameSettings.m_asCategoryVolumes[0] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[1] != g_GameSettings.m_asCategoryVolumes[1] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[2] != g_GameSettings.m_asCategoryVolumes[2] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[3] != g_GameSettings.m_asCategoryVolumes[3] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[4] != g_GameSettings.m_asCategoryVolumes[4] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[5] != g_GameSettings.m_asCategoryVolumes[5] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[6] != g_GameSettings.m_asCategoryVolumes[6] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[7] != g_GameSettings.m_asCategoryVolumes[7] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[8] != g_GameSettings.m_asCategoryVolumes[8] ||
			g_SaveGameState[g_sSaveGameSlot].sCategoryVolume[9] != g_GameSettings.m_asCategoryVolumes[9] 
		)
		{
			if (g_pvrRenderer->m_szDrawMode == PVR::cRenderMgr::Batched && MAPLE::cMapleVmu::m_pActiveVmu != nullptr && MAPLE::cMapleVmu::m_pActiveVmu->Status() == MAPLE::cMapleModule::Connected)
			{
				float r = g_pvrRenderer->m_fGlobalRScale;
				float g = g_pvrRenderer->m_fGlobalGScale;
				float b = g_pvrRenderer->m_fGlobalBScale;
				g_pvrRenderer->DrawQuad(g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_SPLASH_THIRD), 0, 0, 640, 480, PVR::cRenderMgr::LayerBg);

				g_pvrRenderer->m_fGlobalRScale = 1.f;
				g_pvrRenderer->m_fGlobalGScale = 1.f;
				g_pvrRenderer->m_fGlobalBScale = 1.f;

				RPrint print;								
				print.SetFont(30, &g_fontPostal);
				print.SetColorRGB(192, 0, 0);
				print.SetEffectAbs(RPrint::SHADOW_X, 1);
				print.SetEffectAbs(RPrint::SHADOW_Y, 1);
				print.SetWordWrap(TRUE);
				int16_t sOffsetX = 0;
				switch (g_GameSettings.m_cActiveLanguage)
				{
					case 0:
						sOffsetX = 260;
					break;
					case 1:
						sOffsetX = 235;
					break;
					case 2:
						sOffsetX = 155;
					break;
					case 3:
						sOffsetX = 175;
					break;
					case 4:
						sOffsetX = 175;
					break;
					case 5:
						sOffsetX = 215;
					break;
				}
				print.SetColumn(sOffsetX, (g_GameSettings.m_cCutSceneUnderscan ? 390 : 420), 410, 50);
				print.printPVR(0, 0, g_pszSaving[g_GameSettings.m_cActiveLanguage]);	

				g_pvrRenderer->DrawGuiText();			
				DoMenuOutput();
				g_pvrRenderer->RenderBatchedFrame();
				g_pvrRenderer->FlushBatchedDrawQueues();
				g_pvrRenderer->m_pGuiTextString->Flush();

				g_pvrRenderer->m_fGlobalRScale = r;
				g_pvrRenderer->m_fGlobalGScale = g;
				g_pvrRenderer->m_fGlobalBScale = b;
			}			
		}
	}

	if (sMenuItem == -1)
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	else
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);

	return bAcceptChoice;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called when the controls menu is initialized.
//
////////////////////////////////////////////////////////////////////////////////
static int16_t ControlsInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit)			 // TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.
	
	pmenuCur->menuheader.pszHeaderText = g_pszControlsMenu_Title[g_GameSettings.m_cActiveLanguage];

	sprintf(controllerNames[0], "%s A", g_pszController[g_GameSettings.m_cActiveLanguage]);
	sprintf(controllerNames[1], "%s B", g_pszController[g_GameSettings.m_cActiveLanguage]);
	sprintf(controllerNames[2], "%s C", g_pszController[g_GameSettings.m_cActiveLanguage]);
	sprintf(controllerNames[3], "%s D", g_pszController[g_GameSettings.m_cActiveLanguage]);

	pmenuCur->ami[0].pszText = controllerNames[0]; 
	pmenuCur->ami[1].pszText = controllerNames[1];
	pmenuCur->ami[2].pszText = controllerNames[2];
	pmenuCur->ami[3].pszText = controllerNames[3];

	if (sInit != FALSE)
	{							
		g_cActiveController = 0;
	}
	else
	{					
		
	}

	return sRes;
}

////////////////////////////////////////////////////////////////////////////////
//
// Call when a selection is changed or made on the controls menu.
//
////////////////////////////////////////////////////////////////////////////////
static bool ControlsChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		// Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)		// Item chosen.
{
	bool bAcceptChoice = true; // Assume accepting.

	// Audible Feedback.
	if (sMenuItem == -1)
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	else
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);

	g_cActiveController = sMenuItem;		

	return bAcceptChoice;
}


////////////////////////////////////////////////////////////////////////////////
//
// Called when the controls menu is initialized.
//
////////////////////////////////////////////////////////////////////////////////
static int16_t ControlsOptionsInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			 // Current menu.
	int16_t sInit)			 // TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.
	MAPLE::cMapleController *pController = rspGetJoy(g_cActiveController);	
	pmenuCur->menuheader.pszHeaderText = g_pszControlsMenu_Title[g_GameSettings.m_cActiveLanguage];

	pmenuCur->ami[0].pszText = g_pszInputAssisted[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[1].pszText = g_pszInputAssistedAlt[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[2].pszText = g_pszInputWingman[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[3].pszText = g_pszInputFineAim[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[4].pszText = g_pszInputTwinStick[g_GameSettings.m_cActiveLanguage];

	if (sInit != FALSE)
	{	
		RGuiImage *pgi = new RGuiImage;
		
		pgi->offsetPvrX = 60;
		pgi->offsetPvrY = 60;

		pgi->Compose(g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_CONTROLLER));

		if (pController->m_cControlSchemeId == CONTROL_SCHEME_TWINSTICK)
		{
			pmenuCur->ami[0].sEnabled = false;
			pmenuCur->ami[1].sEnabled = false;
			pmenuCur->ami[2].sEnabled = false;
			pmenuCur->ami[3].sEnabled = false;
			pmenuCur->ami[4].sEnabled = true;
			pmenuCur->menuautoitems.sDefaultItem = 4;
			ControlOptionsTwinstick(pgi);
		}			
		else
		{
			pmenuCur->ami[0].sEnabled = true;
			pmenuCur->ami[1].sEnabled = true;
			pmenuCur->ami[2].sEnabled = true;
			pmenuCur->ami[3].sEnabled = true;
			pmenuCur->ami[4].sEnabled = false;
			switch (g_GameSettings.m_sControllerSet[g_cActiveController])
			{
				case 0:
					pmenuCur->menuautoitems.sDefaultItem = 0;
					ControlOptionsAssisted(pgi);				
					break;
				case 1:
					pmenuCur->menuautoitems.sDefaultItem = 1;				
					ControlOptionsAssisted(pgi, true);
					break;
				case 2:
					pmenuCur->menuautoitems.sDefaultItem = 2;
					ControlOptionsWingman(pgi);				
					break;
				case 3:
					pmenuCur->menuautoitems.sDefaultItem = 3;
					ControlOptionsWingman(pgi, true);
					break;			
			}
		}
		
		pmenuCur->ami[0].pgui = pgi;
	}
	else
	{						
		if (MAPLE::cMapleVmu::m_pActiveVmu != nullptr && MAPLE::cMapleVmu::m_pActiveVmu->Status() == MAPLE::cMapleModule::Connected && pController->m_cControlSchemeId != CONTROL_SCHEME_TWINSTICK)		
		{				
			if (g_GameSettings.m_sControllerSet[g_cActiveController] != g_SaveGameState[g_sSaveGameSlot].sControllerSet[g_cActiveController])
			{			
				Game_SavePlayersPrefs(MAPLE::cMapleVmu::m_pActiveVmu, g_sSaveGameSlot, MAPLE::cMapleVmu::m_pszSaveAppName);
			}									
		}		

		delete pmenuCur->ami[0].pgui;		
	}

	return sRes;
}

////////////////////////////////////////////////////////////////////////////////
//
// Call when a selection is changed or made on the controls menu.
//
////////////////////////////////////////////////////////////////////////////////
static bool ControlsOptionsChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		// Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)		// Item chosen.
{
	bool bAcceptChoice = true; // Assume accepting.

	// Audible Feedback.
	if (sMenuItem == -1)
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
	else
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);
		
	switch (sMenuCurr)
	{
		case 0:		
		((RGuiImage*)(pmenuCurrent->ami[0].pgui))->m_pGuiTextString->Flush();
		ControlOptionsAssisted(((RGuiImage*)(pmenuCurrent->ami[0].pgui)));
		g_GameSettings.m_sControllerSet[g_cActiveController] = int8_t(sMenuCurr);
		break;
		case 1:		
		((RGuiImage*)(pmenuCurrent->ami[0].pgui))->m_pGuiTextString->Flush();	
		ControlOptionsAssisted(((RGuiImage*)(pmenuCurrent->ami[0].pgui)), true);
		g_GameSettings.m_sControllerSet[g_cActiveController] = int8_t(sMenuCurr);							
		break;						
		case 2:		
		((RGuiImage*)(pmenuCurrent->ami[0].pgui))->m_pGuiTextString->Flush();	
		ControlOptionsWingman(((RGuiImage*)(pmenuCurrent->ami[0].pgui)));
		g_GameSettings.m_sControllerSet[g_cActiveController] = int8_t(sMenuCurr);							
		break;			
		case 3:		
		((RGuiImage*)(pmenuCurrent->ami[0].pgui))->m_pGuiTextString->Flush();
		ControlOptionsWingman(((RGuiImage*)(pmenuCurrent->ami[0].pgui)), true);
		g_GameSettings.m_sControllerSet[g_cActiveController] = int8_t(sMenuCurr);							
		break;		
		case 4:				
		((RGuiImage*)(pmenuCurrent->ami[0].pgui))->m_pGuiTextString->Flush();
		ControlOptionsTwinstick(((RGuiImage*)(pmenuCurrent->ami[0].pgui)));
		//g_GameSettings.m_sControllerSet[g_cActiveController] = sMenuCurr;
		break;		
	}
	
	MAPLE::cMapleController *pController = rspGetJoy(g_cActiveController);
	
	pController->m_cControlSchemeId = (pController->IsExtended()  ? CONTROL_SCHEME_TWINSTICK : g_GameSettings.m_sControllerSet[g_cActiveController]);

	if (pController->m_cControlSchemeId != CONTROL_SCHEME_TWINSTICK && g_pvrRenderer->m_szDrawMode == PVR::cRenderMgr::Batched && MAPLE::cMapleVmu::m_pActiveVmu != nullptr && MAPLE::cMapleVmu::m_pActiveVmu->Status() == MAPLE::cMapleModule::Connected)	
	{		
		if (sMenuCurr == 5 && g_GameSettings.m_sControllerSet[g_cActiveController] != g_SaveGameState[g_sSaveGameSlot].sControllerSet[g_cActiveController])
		{
			float r = g_pvrRenderer->m_fGlobalRScale;
			float g = g_pvrRenderer->m_fGlobalGScale;
			float b = g_pvrRenderer->m_fGlobalBScale;
			g_pvrRenderer->DrawQuad(g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_SPLASH_THIRD), 0, 0, 640, 480, PVR::cRenderMgr::LayerBg);

			g_pvrRenderer->m_fGlobalRScale = 1.f;
			g_pvrRenderer->m_fGlobalGScale = 1.f;
			g_pvrRenderer->m_fGlobalBScale = 1.f;

			RPrint print;								
			print.SetFont(30, &g_fontPostal);
			print.SetColorRGB(192, 0, 0);
			print.SetEffectAbs(RPrint::SHADOW_X, 1);
			print.SetEffectAbs(RPrint::SHADOW_Y, 1);
			print.SetWordWrap(TRUE);
			int16_t sOffsetX = 0;
				switch (g_GameSettings.m_cActiveLanguage)
				{
					case 0:
						sOffsetX = 260;
					break;
					case 1:
						sOffsetX = 235;
					break;
					case 2:
						sOffsetX = 155;
					break;
					case 3:
						sOffsetX = 175;
					break;
					case 4:
						sOffsetX = 175;
					break;
					case 5:
						sOffsetX = 215;
					break;
				}
				print.SetColumn(sOffsetX, (g_GameSettings.m_cCutSceneUnderscan ? 390 : 420), 410, 50);
			print.printPVR(0, 0, g_pszSaving[g_GameSettings.m_cActiveLanguage]);

			g_pvrRenderer->DrawGuiText();			
			DoMenuOutput();
			g_pvrRenderer->RenderBatchedFrame();
			g_pvrRenderer->FlushBatchedDrawQueues();
			g_pvrRenderer->m_pGuiTextString->Flush();

			g_pvrRenderer->m_fGlobalRScale = r;
			g_pvrRenderer->m_fGlobalGScale = g;
			g_pvrRenderer->m_fGlobalBScale = b;
		}				
	}

	return bAcceptChoice;
}


////////////////////////////////////////////////////////////////////////////////
//
// Called to init or kill the multiplyaer options menu.
//
////////////////////////////////////////////////////////////////////////////////
static int16_t MultiOptionsInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				 // Current menu.
	int16_t sInit)				 // TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.		
	g_sSelectedGameMode = -1;
	g_sSelectedSingleLevel = -1;
	g_sSelectedChallengeMode = -1;
	g_sSelectedChallengeLevel = -1;

	pmenuCur->menuheader.pszHeaderText = g_pszStartSinglePlayerMenu_Coop[g_GameSettings.m_cActiveLanguage];

	if (sInit != FALSE)
	{
		int16_t sMenuItem = 0;	
		g_sActiveMenuController = 0;	
		g_bLimitMenuInput = true;
		g_sActivePlayers = PLAYER_1;
		pmenuCur->ami[5].sEnabled = 0;
		g_cCoopPlayerColours[0] = 0;
		g_cCoopPlayerColours[1] = 0;
		g_cCoopPlayerColours[2] = 1;
		g_cCoopPlayerColours[3] = 2;
					
		for (int16_t i = 0; i < MAPLE_CONTROLLER_MAX && sRes == 0 && i < (NUM_ELEMENTS(pmenuCur->ami) - 2); i++)
		{
			if (rspGetResourceInstance(&g_resmgrShell, PLAYER_COLOR_GUI_FILE, (RTxt **)&(pmenuCur->ami[i].pgui)) == 0)
			{
				RTxt *ptxt = (RTxt *)(pmenuCur->ami[i].pgui);		
				// Set the text from the INI setting. Note that we are changing a
				// resource!
				// todo magic numbers
				ptxt->widthPvr = 175;
				ptxt->heightPvr = 23;
				ptxt->tex = g_pvrRenderer->m_TextureRepository.Get(PVR_TEXTURE_MENU_TEXT_BG);
				ptxt->m_sOffsetX = 3;
				ptxt->m_sOffsetY = 2;
				if (i == 0)
				{
					ptxt->SetText("%s", CGameSettings::ms_apszPlayerColorDescriptions[i][g_GameSettings.m_cActiveLanguage]);
				}
				else
				{
					ptxt->SetText(g_pszPressStart[g_GameSettings.m_cActiveLanguage]);
				}
				
				ptxt->m_pprint->SetFont(19, &g_fontBig);
				ptxt->Compose();
				ptxt->isPvr = true;
			}
			else
			{
				TRACE("MultiOptionsInit(): rspGetResource() failed.\n");
				sRes = 2;
				break;
			}		
		}	

		MultiPlayerOptions(((RTxt*)pmenuCur->ami[0].pgui), PLAYER_1);
	}
	else
	{
		g_bLimitMenuInput = false;
		int16_t sMenuItem = 0;
		for (int16_t i = 0; i < MAPLE_CONTROLLER_MAX && sRes == 0 && i < (NUM_ELEMENTS(pmenuCur->ami) - 2); i++)
		{			
			// If this resource was allocated . . .
			if (pmenuCur->ami[i].pgui)
			{
				// Release resource.
				rspReleaseResourceInstance(&g_resmgrShell, &(pmenuCur->ami[i].pgui));
			}

			pmenuCur->ami[i].pgui = NULL;
		}		
	}
	
	return sRes;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called when a choice is made or changed on the multiplayer options menu.
//
////////////////////////////////////////////////////////////////////////////////
static bool MultiOptionsChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCur,				// Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)			// Item chosen.
{
	bool bAcceptChoice = true; // Assume accepting.
	((RTxt*)pmenuCur->ami[0].pgui)->m_pGuiTextString->Flush();
	((RTxt*)pmenuCur->ami[0].pgui)->Compose();

	switch (g_sActiveMenuController)
	{
		case 0:
		g_sActivePlayers |= PLAYER_1;
		break;
		case 1:
		g_sActivePlayers |= PLAYER_2;
		break;
		case 2:
		g_sActivePlayers |= PLAYER_3;
		break;
		case 3:
		g_sActivePlayers |= PLAYER_4;
		break;
	}

	switch (sMenuItem)
	{
		case 0:		
		case 1:		
		case 2:		
		case 3:									
			g_cCoopPlayerColours[g_sActiveMenuController]++;

			// Increment and check to make sure we have a description and we have such a color . . .		
			if (g_cCoopPlayerColours[g_sActiveMenuController] >= CGameSettings::ms_sNumPlayerColorDescriptions || g_cCoopPlayerColours[g_sActiveMenuController] >= CDude::MaxTextures)
			{
				g_cCoopPlayerColours[g_sActiveMenuController] = 0;
			}
			// Set the text from the INI setting. Note that we are changing a
			// resource!			
			((RTxt*)pmenuCur->ami[g_sActiveMenuController].pgui)->SetText("%s", CGameSettings::ms_apszPlayerColorDescriptions[g_cCoopPlayerColours[g_sActiveMenuController]][g_GameSettings.m_cActiveLanguage]);
			((RTxt*)pmenuCur->ami[g_sActiveMenuController].pgui)->Compose();												
		break;
	}
		
	if (g_sActivePlayers > PLAYER_1)
	{
		pmenuCur->ami[5].sEnabled = 1;
	}	
		
	MultiPlayerOptions(((RTxt*)pmenuCur->ami[0].pgui), g_sActivePlayers);			

	// Audible Feedback.
	if (sMenuItem == -1)
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);			
	else
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);

	return bAcceptChoice;
}


////////////////////////////////////////////////////////////////////////////////
//
// Called to init or kill the level select.
//
////////////////////////////////////////////////////////////////////////////////
static int16_t LoadLevelInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,			  // Current menu.
	int16_t sInit)			  // TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.

	pmenuCur->menuheader.pszHeaderText = g_pszStartSinglePlayerMenu_LoadLevel[g_GameSettings.m_cActiveLanguage];
	g_sSelectedGameMode = -1;	
	g_sSelectedChallengeMode = -1;
	g_sSelectedChallengeLevel = -1;

	if (sInit == TRUE)
	{
		sRes = Play_InitLevelSelectMenu(pmenuCur);
	}
		
	return sRes;
}

////////////////////////////////////////////////////////////////////////////////
//
// Called when a choice is made on the level select.
//
////////////////////////////////////////////////////////////////////////////////
static bool LoadLevelChoice( // Returns true to accept, false to deny choice.
	Menu *pmenuCurrent,		 // Current menu.
	int16_t sMenuItem, int16_t sMenuCurr)		 // Item chosen or -1 for change of focus.
{
	bool bAcceptChoice = true; // Assume accepting.

	// Audible Feedback.
	if (sMenuItem == -1)
	{
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
		g_sSelectedSingleLevel = -1;
	}		
	else
	{
		PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);
		if (pmenuCurrent->ami[sMenuItem].sEnabled == TRUE)
		{
			TRACE("sMenuItem = %d\n", sMenuItem);
			g_sSelectedSingleLevel = sMenuItem;			
		}
	}

	return bAcceptChoice;
}

////////////////////////////////////////////////////////////////////////////////
// GUI callbacks.



////////////////////////////////////////////////////////////////////////////////
//
// Callback to update pain frequency adjustment.
//
////////////////////////////////////////////////////////////////////////////////
static void PainFrequencyScrollUpdate( // Returns nothing.
	RScrollBar *psb)				   // Scrollbar that got updated.
{
	ASSERT(psb != NULL);

	int32_t lMin, lMax;
	psb->GetRange(&lMin, &lMax);
	g_GameSettings.m_sPainFrequency = (int8_t)((((psb->GetPos() - lMin) * 16) / (lMax - lMin)));
	// work around weird range issue halfway through slider
	if (g_GameSettings.m_sPainFrequency < 10)
		g_GameSettings.m_sPainFrequency++;
	TRACE("g_GameSettings.m_sPainFrequency = %d\n", g_GameSettings.m_sPainFrequency);
}

////////////////////////////////////////////////////////////////////////////////
//
// Callback from volume sliders to update corresponding volume adjustment.
//
////////////////////////////////////////////////////////////////////////////////
static void VolumesScrollUpdate( // Returns nothing.
	RScrollBar *psb)			 // Scrollbar that got updated.
{
	ASSERT(psb != NULL);

	SampleMaster::SoundCategory sc = (SampleMaster::SoundCategory)psb->m_ulUserData;
	int32_t lMin, lMax, lRange;
	psb->GetRange(&lMin, &lMax);
	lRange = lMax - lMin;

	// Get volume and scale to user volume.
	g_GameSettings.m_asCategoryVolumes[sc] = (int16_t)(psb->GetPos() * SampleMaster::UserMaxVolume / lRange);

	SetCategoryVolume(sc, g_GameSettings.m_asCategoryVolumes[sc]);

	SampleMasterID *psmid = ms_apsmidVolumeTesters[sc];
	if (psmid == NULL || g_GameSettings.m_bLimitedSampleTesters)
	{
		psmid = ms_apsmidVolumeTesters[0];
	}

	if (ms_siLastSamplePlayed)
	{
		// Abort last one.
		AbortSample(ms_siLastSamplePlayed);
	}

	// Play a sample in that category.
	PlaySample(					 // Returns RSnd used to play the sample.
								 // Does not fail.
		*psmid,					 // In:  Identifier of sample you want played.
		sc,						 // In:  Sound Volume Category for user adjustment
		255,					 // In:  Initial Sound Instance Volume (0 - 255)
		&ms_siLastSamplePlayed); // Out: Handle for adjusting sound volume

	// Get val indicator.
	RGuiItem *pguiVal = psb->GetItemFromId(GUI_ID_VOLUME_VAL);
	if (pguiVal)
	{
		pguiVal->SetText("%d", g_GameSettings.m_asCategoryVolumes[sc]);
		pguiVal->Compose();
	}
}


extern Menu menuNoMemoryCard =
	{
		NO_MEMORY_CARD_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_MD,			  // menu x, y, w, h
			-120,					  // menu header x offset
			MENU_HEAD_Y_MD,			  // menu header y offset
			MENU_ITEM_X_MD,			  // menu items x offset
			MENU_ITEM_Y_MD,			  // menu items y offset
			MENU_ITEM_SPACE_Y_MD,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_MD, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.			
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			NoMemoryCardMenuInit,	// Called before menu is initialized.
			NoMemoryCardMenuChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.			
		{
			// pszText,								sEnabled,	pmenu,					pgui
			{
				"",
				FALSE,
				NULL,
				NULL,
			},
			{
				"",
				FALSE,
				NULL,
				NULL,
			},
			{
				"",
				FALSE,
				NULL,
				NULL,
			},
			{
				"", // todo localisation
				TRUE,
				NULL,
				NULL,
			},
			{
				"", // todo localisation
				TRUE,
				&menuMain,
				NULL,
			},		
			{
				"",
				FALSE,
				NULL,
				NULL,
			},
			NULL // Terminates list.
		},
};

// todo
static int16_t VideoMenuInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit)				// TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.
	pmenuCur->menuheader.pszHeaderText = g_pszVideoMenu_Title[g_GameSettings.m_cActiveLanguage];
	return sRes;
}

static bool VideoMenuChoice(Menu *pmenuCurrent, int16_t sMenuItem, int16_t sMenuCurr)
{	
	if (sMenuItem == -1) // focus change.
	{
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
		return true;
	}
	
	PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);
		
	switch (sMenuItem)
	{
		case 0:
			// Set 50 hz
			SetPal60(false);
			return true;
		break;
		case 1:		
			// Set 60 hz
			SetPal60(true);
			return true;
		break;
		case 2:
			// Set 60 hz
			SetPal60(true);
			g_GameSettings.m_cCutSceneUnderscan = true;
			return true;			
		break;
		case 5:
			return false;
		break;
	}	
}


// todo
static int16_t LanguageMenuInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit)				// TRUE, if initializing; FALSE, if killing.
{	
	return 0;
}


static bool LanguageMenuChoice(Menu *pmenuCurrent, int16_t sMenuItem, int16_t sMenuCurr)
{	
	if (sMenuItem == -1) // focus change.
	{
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);		
	}
	
	PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);				

	switch (sMenuItem)
	{
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
			SetLanguage(sMenuItem);			
			return true; // accept choice.
		break;
		case 6:
		return false; // accept choice.
		break;
	}	
}

// todo
static int16_t NoMemoryCardMenuInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit)				// TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.		

	int8_t cSize = 0;
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 2:						
		case 3:		
		case 4:
			cSize = 1;			
		break;		
	}
	MenuResize(pmenuCur, cSize);	
	pmenuCur->menuheader.pszHeaderText = g_pszNoMemoryCard[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[0].pszText = g_pszGameNoSave[g_GameSettings.m_cActiveLanguage][0];
	pmenuCur->ami[1].pszText = g_pszGameNoSave[g_GameSettings.m_cActiveLanguage][1];	
	pmenuCur->ami[3].pszText = g_pszRecheckMemoryCard[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[4].pszText = g_pszContinueNoSave[g_GameSettings.m_cActiveLanguage];
	return sRes;
}


static bool NoMemoryCardMenuChoice(Menu *pmenuCurrent, int16_t sMenuItem, int16_t sMenuCurr)
{	
	if (sMenuItem == -1) // focus change.
	{
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);
		return true;
	}

	if (sMenuItem == 3)
	{
		if (MAPLE::cMapleVmu::GetCount()) 
		{
			pmenuCurrent->ami[sMenuItem].pmenu = &menuPickMemoryCard;
		}
		else
		{
			pmenuCurrent->ami[sMenuItem].pmenu = pmenuCurrent;
		}
	}

	PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);
	
	return true;			   // accept choice.
}



extern Menu menuPickMemoryCard =
	{
		PICK_MEMORY_CARD_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_MD,			  // menu x, y, w, h
			-120,					  // menu header x offset
			MENU_HEAD_Y_MD,			  // menu header y offset
			MENU_ITEM_X_MD,			  // menu items x offset
			MENU_ITEM_Y_MD,			  // menu items y offset
			MENU_ITEM_SPACE_Y_MD,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_MD, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			PickMemoryCardMenuInit,	// Called before menu is initialized.
			PickMemoryCardMenuChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},
		
		// Menu items.
		// Filled in during init.
		{
			// pszText,						sEnabled,	pmenu,		pgui
			NULL // Terminates list.
		},
};

static int16_t PickMemoryCardMenuInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit)				// TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.
	int8_t itemNum = 0;

	int8_t cSize = 0;
	switch (g_GameSettings.m_cActiveLanguage)
	{
		case 3:
		case 4:
			cSize = 1;
		break;
	}
	MenuResize(pmenuCur, cSize);

	pmenuCur->menuheader.pszHeaderText = g_pszChooseMemoryCard[g_GameSettings.m_cActiveLanguage];

	if (sInit != FALSE)
	{				
		MAPLE::cMapleVmu::m_pActiveVmu = nullptr;		
	
		for (int index = 0; index < MAPLE_MEMCARD_MAX; index++)
		{
			g_pMapleVmuIndices[index] = nullptr;
			g_bDynamicStringIndices[index] = false;
			pmenuCur->ami[index].sEnabled = 0;
		}

		g_cMapleVmuCount = MAPLE::cMapleVmu::GetCount();
		for (int index = 0; index < MAPLE_CONTROLLER_MAX; index++)
		{
			MAPLE::cMapleController *controller = rspGetJoy(index);
			if (controller && controller->Status() != MAPLE::cMapleController::ControllerDisconnected)
			{
				char label[50];			
				if (controller->m_pMemCards[MAPLE_MEMCARD_1]->Status() == MAPLE::cMapleModule::Connected)
				{										
					g_pMapleVmuIndices[itemNum] = controller->m_pMemCards[MAPLE_MEMCARD_1];
					sprintf(label, "%s %c - %s #1", g_pszController[g_GameSettings.m_cActiveLanguage], (controller->Port() - 32), g_pszPort[g_GameSettings.m_cActiveLanguage]);					
					g_bDynamicStringIndices[itemNum] = true;
					pmenuCur->ami[itemNum].sEnabled = 1;	
					pmenuCur->ami[itemNum].pmenu = &menuPickFile;										
					pmenuCur->ami[itemNum++].pszText = strdup(label);
				}
				else if (controller->m_pMemCards[MAPLE_MEMCARD_1]->Status() == MAPLE::cMapleModule::Error)
				{
					sprintf(label, "%s %c - %s #1 - ERR", g_pszController[g_GameSettings.m_cActiveLanguage], (controller->Port() - 32), g_pszPort[g_GameSettings.m_cActiveLanguage]);					
					g_bDynamicStringIndices[itemNum] = true;
					pmenuCur->ami[itemNum].sEnabled = 0;
					pmenuCur->ami[itemNum].pmenu = &menuPickFile;
					pmenuCur->ami[itemNum++].pszText = strdup(label);
				}
								
				if (controller->m_pMemCards[MAPLE_MEMCARD_2]->Status() == MAPLE::cMapleModule::Connected)
				{					
					g_pMapleVmuIndices[itemNum] = controller->m_pMemCards[MAPLE_MEMCARD_2];
					sprintf(label, "%s %c - %s #2", g_pszController[g_GameSettings.m_cActiveLanguage], (controller->Port() - 32), g_pszPort[g_GameSettings.m_cActiveLanguage]);					
					g_bDynamicStringIndices[itemNum] = true;
					pmenuCur->ami[itemNum].sEnabled = 1;		
					pmenuCur->ami[itemNum].pmenu = &menuPickFile;													
					pmenuCur->ami[itemNum++].pszText = strdup(label);					
				}				
				else if (controller->m_pMemCards[MAPLE_MEMCARD_2]->Status() == MAPLE::cMapleModule::Error)
				{
					sprintf(label, "%s %c - %s #2 - ERR", g_pszController[g_GameSettings.m_cActiveLanguage], (controller->Port() - 32), g_pszPort[g_GameSettings.m_cActiveLanguage]);					
					g_bDynamicStringIndices[itemNum] = true;
					pmenuCur->ami[itemNum].sEnabled = 0;	
					pmenuCur->ami[itemNum].pmenu = &menuPickFile;										
					pmenuCur->ami[itemNum++].pszText = strdup(label);
				}																					
			}
		}

		g_bDynamicStringIndices[itemNum] = true;
		pmenuCur->ami[itemNum].sEnabled = 1;		
		pmenuCur->ami[itemNum].pmenu = &menuMain;		
		pmenuCur->ami[itemNum++].pszText = strdup(g_pszContinueNoSave[g_GameSettings.m_cActiveLanguage]);

		// Blank, to stop "continue" option being seleted on cancel.
		pmenuCur->ami[itemNum].sEnabled = 0;		
		pmenuCur->ami[itemNum++].pszText = "";		
		
	}
	else
	{
		// De-allocate strdup strings
		for (int index = 0; index < MAPLE_MEMCARD_MAX + 1; index++)
		{						
			if (pmenuCur->ami[index].pszText && g_bDynamicStringIndices[index])
			{
				free(pmenuCur->ami[index].pszText);
				g_bDynamicStringIndices[index] = false;
			}			
		}
	}

	return sRes;
}


static bool PickMemoryCardMenuChoice(Menu *pmenuCurrent, int16_t sMenuItem, int16_t sMenuCurr)
{
	if (sMenuItem == -1) // focus change.
	{
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);		
		return false;
	}
	
	PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);

	if (sMenuItem == g_cMapleVmuCount) // Continue without saving.
	{
		g_sSaveGameSlot = -1;				
		return true;
	}

	if (g_pMapleVmuIndices[sMenuItem]->Status() == MAPLE::cMapleModule::Connected)
	{						
		MAPLE::cMapleVmu::m_pActiveVmu = g_pMapleVmuIndices[sMenuItem];
	}
	else
	{
		// The memory card has been removed - reload the menu.
		if (MAPLE::cMapleVmu::GetCount())
		{
			pmenuCurrent->ami[sMenuItem].pmenu = pmenuCurrent;
		}
		else
		{
			pmenuCurrent->ami[sMenuItem].pmenu = &menuNoMemoryCard;
			MAPLE::cMapleVmu::m_pActiveVmu = nullptr;
		}
		
		for (int index = 0; index < MAPLE_MEMCARD_MAX; index++)
		{
			g_pMapleVmuIndices[index] = nullptr;			
		}		
	}	
	return true;
}


extern Menu menuPickFile =
	{
		PICK_FILE_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_LG,			  // menu x, y, w, h
			-120,					  // menu header x offset
			MENU_HEAD_Y_LG,			  // menu header y offset
			MENU_ITEM_X_LG,			  // menu items x offset
			MENU_ITEM_Y_LG,			  // menu items y offset
			MENU_ITEM_SPACE_Y_LG,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_LG, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			PickFileMenuInit,	// Called before menu is initialized.
			PickFileMenuChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.
		// Filled in by Init.
		{
			// pszText,						sEnabled,	pmenu,		pgui
			NULL // Terminates list.
		},
};


// todo
static int16_t PickFileMenuInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit)				// TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.	
	g_PickFileState = 0;

	pmenuCur->menuheader.pszHeaderText = g_pszLoadSave[g_GameSettings.m_cActiveLanguage];

	if (sInit != FALSE)
	{		
		MAPLE::cMapleVmu *pActiveVmu = MAPLE::cMapleVmu::m_pActiveVmu;
		for (int8_t index = 0; index < 7; index++)
		{
			g_bDynamicStringIndices[index] = false;
		}

		g_sSaveGameSlot = -1;

		if (pActiveVmu->Status() == MAPLE::cMapleModule::Connected)
		{
			// POSTAL save file found.
			if (pActiveVmu->FileExists(MAPLE::cMapleVmu::m_pszSaveAppName) && Game_ReadGlobalSaveStates()) // todo: #define
			{								
				g_PickFileState = 1;

				for (int8_t index = 0; index < MAPLE_SAVE_FILE_MAX; index++)
				{	
					char label[100];			
					char realm[30];
					char *difficulty;
					
					pmenuCur->ami[index].sEnabled = 1;		
					pmenuCur->ami[index].pmenu = &menuMain;

					if (g_SaveGameState[index].sRealmNum[0] == 0 && g_SaveGameState[index].sRealmNum[1] == 0)
					{
						pmenuCur->ami[index].pszText = g_pszNewSave[g_GameSettings.m_cActiveLanguage]; // todo: localisation
					}
					else
					{						
						bool bCoopSave = (g_SaveGameState[index].sRealmNum[1] > g_SaveGameState[index].sRealmNum[0]);
						int8_t cOffset = (bCoopSave) ? 1 : 0;

						switch (g_SaveGameState[index].sDifficulty[cOffset])
						{
							case 1:
							difficulty = g_pszGameOptionsMenu_Easy[g_GameSettings.m_cActiveLanguage];
							break;
							case 4:
							difficulty = g_pszGameOptionsMenu_Medium[g_GameSettings.m_cActiveLanguage];
							break;
							case 7:
							difficulty = g_pszGameOptionsMenu_Hard[g_GameSettings.m_cActiveLanguage];
							break;
							case 11:
							difficulty = g_pszGameOptionsMenu_Nightmare[g_GameSettings.m_cActiveLanguage];
							break;
						}

						switch (g_SaveGameState[index].sRealmNum[cOffset])
						{							
							case 1:
								if (g_SaveGameState[index].sAction[cOffset] == 12)
								{
									strcpy(realm, "Shanty Town");
								}
								else if (g_SaveGameState[index].sAction[cOffset] == 13)
								{
									strcpy(realm, "Osaka");
								}
								else
								{
									strcpy(realm, "The Truckstop");
								}								
							break;
							case 2:								
								if (g_SaveGameState[index].sAction[cOffset] == 12)
								{
									strcpy(realm, "Earthquake");
								}								
								else
								{
									strcpy(realm, "The Outskirts");
								}			
							break;		
							case 3:
								if (g_SaveGameState[index].sAction[cOffset] == 12)
								{
									strcpy(realm, "La Palamino Resort");
								}								
								else
								{
									strcpy(realm, "Parade of Disasters");
								}									
							break;	
							case 4:
								strcpy(realm, "The Bridge");
							break;
							case 5:
								strcpy(realm, "The Mine");
							break;	
							case 6:
								strcpy(realm, "The Junk Yard");
							break;
							case 7:
								strcpy(realm, "The Trailer Park");
							break;
							case 8:
								strcpy(realm, "The Train");
							break;
							case 9:
								strcpy(realm, "The Farm");
							break;
							case 10:
								strcpy(realm, "The Construction Site");
							break;
							case 11:
								strcpy(realm, "The Ghetto");
							break;	
							case 12:
								strcpy(realm, "The City");
							break;
							case 13:
								strcpy(realm, "Central Park");
							break;
							case 14:
								strcpy(realm, "The Industrial Complex");
							break;
							case 15:
								strcpy(realm, "Air Force Base");
							break;
							case 16:
								strcpy(realm, "EZ Mart");
							break;
							case 17:
								strcpy(realm, "Shanty Town");
							break;	
							case 18:
								strcpy(realm, "Earthquake");
							break;
							case 19:
								strcpy(realm, "La Palamino Resort");
							break;
							case 20:
								strcpy(realm, "Tokyo");
							break;
							case 21:
								strcpy(realm, "Osaka");
							break;							
						}
						
						if (bCoopSave)
						{
							sprintf(label, "%s (%s %s)", realm, difficulty, g_pszInputCoop[g_GameSettings.m_cActiveLanguage]);							
						}
						else
						{
							sprintf(label, "%s (%s)", realm, difficulty);
						}
						
						pmenuCur->menuitemsfont.sHeight = 19; // ITEM_FONT_HEIGHT todo
						g_bDynamicStringIndices[index] = true;
						pmenuCur->ami[index].pszText = strdup(label); // todo: localisation
					}														
				}				
		
				pmenuCur->ami[MAPLE_SAVE_FILE_MAX].sEnabled = 1;					
				pmenuCur->ami[MAPLE_SAVE_FILE_MAX].pszText = "";
			}		
			// No save file.
			else
			{	
				pmenuCur->menuitemsfont.sHeight = ITEM_FONT_HEIGHT;
				// Not enough free space.				
				if (pActiveVmu->GetFreeBlocks() < SAVE_FILE_BLOCKS)
				{					
					g_PickFileState = -1;					

					pmenuCur->ami[0].sEnabled = 0;
					pmenuCur->ami[0].pszText = g_pszGameNoSpace[g_GameSettings.m_cActiveLanguage][0];
				
					pmenuCur->ami[1].sEnabled = 0;
					pmenuCur->ami[1].pszText = g_pszGameNoSpace[g_GameSettings.m_cActiveLanguage][1];
				
					pmenuCur->ami[2].sEnabled = 0;					
					pmenuCur->ami[2].pszText = g_pszGameNoSpace[g_GameSettings.m_cActiveLanguage][2];										

					pmenuCur->ami[3].sEnabled = 0;
					pmenuCur->ami[3].pszText = "";

					pmenuCur->ami[4].sEnabled = 1;
					pmenuCur->ami[4].pmenu = &menuPickMemoryCard;
					pmenuCur->ami[4].pszText = g_pszChooseAnotherVmu[g_GameSettings.m_cActiveLanguage];

					pmenuCur->ami[5].sEnabled = 1;
					pmenuCur->ami[5].pmenu = &menuMain;
					pmenuCur->ami[5].pszText = g_pszContinueNoSave[g_GameSettings.m_cActiveLanguage];

					pmenuCur->ami[6].sEnabled = 1;
					pmenuCur->ami[6].pszText = "";			
				}
				// We have enough free space.
				else
				{
					g_PickFileState = 2;
					pmenuCur->ami[0].sEnabled = 0;
					pmenuCur->ami[0].pszText = g_pszGameNoSaveFound[g_GameSettings.m_cActiveLanguage];

					pmenuCur->ami[1].sEnabled = 0;
					pmenuCur->ami[1].pszText = "";

					pmenuCur->ami[2].sEnabled = 1;
					pmenuCur->ami[2].pmenu = pmenuCur;
					pmenuCur->ami[2].pszText = g_pszGameNewSave[g_GameSettings.m_cActiveLanguage];

					pmenuCur->ami[3].sEnabled = 1;
					pmenuCur->ami[3].pmenu = &menuMain;
					pmenuCur->ami[3].pszText = g_pszContinueNoSave[g_GameSettings.m_cActiveLanguage];

					pmenuCur->ami[4].sEnabled = 0;
					pmenuCur->ami[4].pszText = "";

					pmenuCur->ami[5].sEnabled = 1;
					pmenuCur->ami[5].pszText = "";
				}												
			}
		}	
		// File save error.	
		else		
		{
			g_PickFileState = 3;
			pmenuCur->ami[0].sEnabled = 0;
			if (pActiveVmu->Status() == MAPLE::cMapleModule::Error)
			{
				pmenuCur->ami[0].pszText = g_pszVmuWriteError[g_GameSettings.m_cActiveLanguage];
			}
			else
			{
				pmenuCur->ami[0].pszText = g_pszVmuRemoved[g_GameSettings.m_cActiveLanguage];
			}
						
			pmenuCur->ami[1].sEnabled = 0;
			pmenuCur->ami[1].pszText = "";		
			
			pmenuCur->ami[2].sEnabled = 1;			
			pmenuCur->ami[2].pmenu = &menuPickMemoryCard;
			pmenuCur->ami[2].pszText = g_pszChooseAnotherVmu[g_GameSettings.m_cActiveLanguage];

			pmenuCur->ami[3].sEnabled = 1;			
			pmenuCur->ami[3].pmenu = &menuMain;
			pmenuCur->ami[3].pszText = g_pszContinueNoSave[g_GameSettings.m_cActiveLanguage];

			pmenuCur->ami[4].sEnabled = 1;
			pmenuCur->ami[4].pszText = "";
		}					
	}
	else
	{
		// De-allocate strdup strings
		for (int index = 0; index < 7; index++)
		{						
			if (pmenuCur->ami[index].pszText && g_bDynamicStringIndices[index])
			{
				free(pmenuCur->ami[index].pszText);
				g_bDynamicStringIndices[index] = false;
			}			
		}
	}

	return sRes;
}


static bool PickFileMenuChoice(Menu *pmenuCurrent, int16_t sMenuItem, int16_t sMenuCurr)
{
	ASSERT(pmenuCurrent == &menuPickFile);

	MAPLE::cMapleVmu *pActiveVmu = MAPLE::cMapleVmu::m_pActiveVmu;		
	bool bWriteError = false;
	
	if (pActiveVmu->Status() == MAPLE::cMapleModule::Disconnected)
	{
		MAPLE::cMapleVmu::m_pActiveVmu = nullptr;
		// todo: return to previous menu
	}

	if (sMenuItem == -1)
	{
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);				
		return true;
	}

	PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);

	// todo: magic numbers
		
	// Choose game save file
	if (g_PickFileState == 1)
	{		
		g_sSaveGameSlot = sMenuItem;
		Game_LoadPlayersSaveSlot(sMenuItem);		
	}

	// Not enough free space
	if (g_PickFileState == -1)
	{		
		// Continue without saving or select another vmu.
		if (sMenuItem == 4 || sMenuItem == 5)
		{
			// Unset any active VMUs
			MAPLE::cMapleVmu::m_pActiveVmu = nullptr;
		}	
	}

	// Create new save file
	if (g_PickFileState == 2)
	{		
		if (sMenuItem == 2)
		{
			bWriteError = (pActiveVmu->Status() == MAPLE::cMapleModule::Connected) ? false : true;
			if (!bWriteError)
			{				
				Game_InitSaveFile();
			}
		}

		// Continue without saving.
		if (sMenuItem == 3)
		{
			// Unset any active VMUs
			MAPLE::cMapleVmu::m_pActiveVmu = nullptr;		
		}
	}

	// File save error
	if (g_PickFileState == 3)
	{
		// Continue without saving or select another vmu.
		if (sMenuItem == 2 || sMenuItem == 3)
		{
			// Unset any active VMUs
			MAPLE::cMapleVmu::m_pActiveVmu = nullptr;			
		}
	}

	return true;			   // accept choice.
}


extern Menu menuMapleDisconnect =
	{
		MAPLE_DISCONNECT_MENU_ID,

		// Position info.
		{
			// x, y, w, h, sPosX, sPosY, sItemSpacingY, sIndicatorSpacingX,
			MENU_RECT_LG,			  // menu x, y, w, h
			-60,					  // menu header x offset
			MENU_HEAD_Y_LG,			  // menu header y offset
			MENU_ITEM_X_LG,			  // menu items x offset
			MENU_ITEM_Y_LG,			  // menu items y offset
			MENU_ITEM_SPACE_Y_LG,	  // vertical space between menu items
			MENU_ITEM_IND_SPACE_X_LG, // horizontal space between indicator and menu items
		},

		// Background info.
		{
			// pszFile, u32BackColor
			MENU_BG_MD,
			MENU_BG_COLOR, // Background color.
			PAL_SET_START, // Starting palette index to set.
			PAL_SET_NUM,   // Number of entries to set.
			PAL_MAP_START, // Starting index of palette entries that can be mapped to.
			PAL_MAP_NUM,   // Number of palette entries that can be mapped to.
		},

		// GUI settings.
		{
			// sTransparent.
			TRUE, // TRUE if GUI is to be BLiT with transparency.
		},

		// Flags.
		(MenuFlags)(MenuPosCenter | MenuBackTiled | MenuItemTextShadow | MenuHeaderTextShadow | MenuHeaderTextCenter),

		// Header and its font info.
		{
			// pszHeaderText, pszFontFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor.
			NULL,
			SMASH_FONT,
			HEAD_FONT_HEIGHT, // Height of font.
			HEAD_COLOR,		  // Text RGBA.
			HEAD_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Font info.
		{
			// pszFile, sHeight, u32ForeColor, u32BackColor, u32ShadowColor
			SMASH_FONT,
			ITEM_FONT_HEIGHT, // Height of font.
			ITEM_COLOR,		  // Text RGBA.
			ITEM_SHADOW_COLOR // Text Shadow RGBA.
		},

		// Menu indicator.
		{
			// pszFile, type
			MENU_INDICATOR,
			RImage::FSPR8,
		},

		// Menu callbacks.
		{
			// fnInit, fnChoice,
			MapleDisconnectMenuInit,	// Called before menu is initialized.
			MapleDisconnectMenuChoice, // Called when item is chosen.
		},

		// Menu auto items.
		{
			// sDefaultItem, sCancelItem,
			0,	// Menu item (index in ami[]) selected initially.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
			-1, // Menu item (index in ami[]) chosen on cancel.
				// Negative indicates distance from number of items
				// (e.g., -1 is the last item).
		},

		// Menu items.			
		{
			// pszText,								sEnabled,	pmenu,					pgui
			{
				"",
				FALSE,
				NULL,
				NULL,
			},		
			{
				"", // todo localisation
				FALSE,
				NULL,
				NULL,
			},
			{
				"OK", // todo localisation
				TRUE,
				NULL,
				NULL,
			},				
			NULL // Terminates list.
		},
};

static int8_t g_MapleDisconnectState = 0;

// todo
static int16_t MapleDisconnectMenuInit( // Returns 0 on success, non-zero to cancel menu.
	Menu *pmenuCur,				// Current menu.
	int16_t sInit)				// TRUE, if initializing; FALSE, if killing.
{
	int16_t sRes = 0; // Assume success.
	pmenuCur->menuheader.pszHeaderText = g_pszDeviceRemoved[g_GameSettings.m_cActiveLanguage];
	pmenuCur->ami[2].pszText = g_pszOK[g_GameSettings.m_cActiveLanguage];

	if (sInit != FALSE)
	{		
		g_MapleDisconnectState = 0;
		char message[50];
		for (int index = 0; index < MAPLE_CONTROLLER_MAX; index++)
		{
			MAPLE::cMapleController *pController = rspGetJoy(index);
			if (pController->Status() == MAPLE::cMapleController::ControllerDisconnected)
			{				
				g_MapleDisconnectState = 1;
				sprintf(message, "%s %c", g_pszReconnectController[g_GameSettings.m_cActiveLanguage], pController->Port() - 32);
				g_bDynamicStringIndices[0] = true;
				pmenuCur->ami[0].pszText = strdup(message); // todo: localisation
				pmenuCur->ami[0].sEnabled = 0;
				break;				
			}
			else if (pController->Status() == MAPLE::cMapleController::VmuDisconnected)
			{		
				g_MapleDisconnectState = 2;
				if (MAPLE::cMapleVmu::m_pActiveVmu != nullptr && MAPLE::cMapleVmu::m_pActiveVmu->Status() == MAPLE::cMapleModule::Error)
				{
					sprintf(message, "%s %c:%d - %s", g_pszMemoryCardFault[g_GameSettings.m_cActiveLanguage], pController->Port() - 32, MAPLE::cMapleVmu::m_pActiveVmu->GetUnit(), g_pszReboot[g_GameSettings.m_cActiveLanguage]);
					g_bDynamicStringIndices[0] = true;
					pmenuCur->ami[0].pszText = strdup(message); // todo: localisation
					pmenuCur->ami[0].sEnabled = 0;
					pmenuCur->ami[1].sEnabled = 0;
					pmenuCur->ami[1].pszText = "";
					pmenuCur->ami[2].sEnabled = 0;
					pmenuCur->ami[2].pszText = "";
					pmenuCur->menuindicator.pszFile = NULL;
				}
				else
				{
					sprintf(message, "%s %c:%d", g_pszReconnectVmu[g_GameSettings.m_cActiveLanguage], pController->Port() - 32, MAPLE::cMapleVmu::m_pActiveVmu->GetUnit());			
					g_bDynamicStringIndices[0] = true;
					pmenuCur->ami[0].pszText = strdup(message); // todo: localisation
					pmenuCur->ami[0].sEnabled = 0;
					if (g_GameSettings.m_cActiveLanguage == 3)
					{
						pmenuCur->menuitemsfont.sHeight = 27;
					}
				}								
				break;	
			}
		}
	}
	else
	{
		// De-allocate strdup strings
		free(pmenuCur->ami[0].pszText);
		g_bDynamicStringIndices[0] = false;
	}

	return sRes;
}

static bool MapleDisconnectMenuChoice(Menu *pmenuCurrent, int16_t sMenuItem, int16_t sMenuCurr)
{		
	if (sMenuItem == -1)
	{
		PlaySample(g_smidMenuItemChange, SampleMaster::UserFeedBack);				
		return true;
	}

	PlaySample(g_smidMenuItemSelect, SampleMaster::UserFeedBack);

	if (g_MapleDisconnectState == 2 && MAPLE::cMapleVmu::m_pActiveVmu != nullptr && 
		(MAPLE::cMapleVmu::m_pActiveVmu->FileExists(MAPLE::cMapleVmu::m_pszSaveAppName) == false || 
		 MAPLE::cMapleVmu::m_pActiveVmu->Status() == MAPLE::cMapleModule::Disconnected || 
		 MAPLE::cMapleVmu::m_pActiveVmu->Status() == MAPLE::cMapleModule::Error))
	{
		return true;
	}

	if (sMenuItem == 2 || sMenuItem == 3)
	{		
		Play_VerifyQuitMenuChoice(pmenuCurrent, 0, 0);
	}

	return true;			   // accept choice.
}





////////////////////////////////////////////////////////////////////////////////
// EOF
////////////////////////////////////////////////////////////////////////////////
