/************************************************************************************
// Chicken Run Memory Card Stuff...
************************************************************************************/

#include "global.h"
#include "main.h"
#include "card.h"
#include "gametext.h"
#include "menu.h"
#include "overlays.h"
#include "maths.h"
#include "options.h"
#include "startup.h"
#include "inventory.h"
#include "sound.h"
#include "pause.h"
#include "islcard.h"
#include "vmucheck.h"

char *cardOURTITLE = "Chicken Run";
int loadSaveNumLines;
int gotoLoad;
int selectedPad = 0,selectedVMU = 0;
TextureType *vmuSprite = NULL,*padSprite = NULL,*vmuSprite2 = NULL;

extern TIM_HEADER *splashImg;

int vmuStatus[4][2] = {-1,-1,-1,-1,-1,-1,-1,-1};
int vmuSpace[4][2] = {-1,-1,-1,-1,-1,-1,-1,-1};
int vmuGame[4][2] = {0,0,0,0,0,0,0,0};
char *portName[4] = {"A","B","C","D"};

#undef NULL
#define NULL 0

SfxSampleType *cardmenusfx1;
SfxSampleType *cardmenusfx2;
SfxSampleType *cardmenusfx3;
char cardmenusfxset;
int cardmenusfx_pitch1;
int cardmenusfx_pitch2;
int cardmenusfx_pitch3;
int cardmenusfx_vol1;
int cardmenusfx_vol2;
int cardmenusfx_vol3;


#if(PALMODE==YES)
	int INIT_DOGCHASE_BEST = 7500;
	int INIT_COGCHASE_BEST = 7500;
	int INIT_ACT1_BEST = (30*60*25);
	int INIT_ACT2_BEST = (90*60*25);
	int INIT_ACT3_BEST = (120*60*25);
#else
	int INIT_DOGCHASE_BEST = 9000;
	int INIT_COGCHASE_BEST = 9000;
	int INIT_ACT1_BEST = (30*60*30);
	int INIT_ACT2_BEST = (90*60*30);
	int INIT_ACT3_BEST = (120*60*30);
#endif

// ===================================================================
// Okay, this is the important one. Current state of the entire game
GameSaveDataType gameSaveData;

// For use during replays of acts. Not saved to card.
GamePlayDataType gamePlayDataMain;

// Points to either the replay one, or to the current one inside the savedata.
GamePlayDataType *gamePlayData = &gamePlayDataMain;

void streetStartChooseOption();
int streetChooseOption(char *msg, char *msg1, char *msg2);
int gameSaveGetCardStatus();
extern GsRVIEW2 currentcamera;

// ===================================================================
// Ascii & SJIS. Lovely.

// LookUp Tables for the Ascii to Sjis Function.
static unsigned short ascii_table[3][2] = {
	{0x824f, 0x30},	/* 0-9  */
	{0x8260, 0x41},	/* A-Z  */
	{0x8281, 0x61},	/* a-z  */
};

// ASCII code to Shift-JIS code transfer table (kigou)
 
static unsigned short ascii_k_table[] = {
	0x8140,		/*   */
	0x8149,		/* ! */
	0x8168,		/* " */
	0x8194,		/* # */
	0x8190,		/* $ */
	0x8193,		/* % */
	0x8195,		/* & */
	0x8166,		/* ' */
	0x8169,		/* ( */
	0x816a,		/* ) */
	0x8196,		/* * */
	0x817b,		/* + */
	0x8143,		/* , */
	0x817c,		/* - */
	0x8144,		/* . */
	0x815e,		/* / */
	0x8146,		/* : */
	0x8147,		/* ; */
	0x8171,		/* < */
	0x8181,		/* = */
	0x8172,		/* > */
	0x8148,		/* ? */
	0x8197,		/* @ */
	0x816d,		/* [ */
	0x818f,		/* \ */
	0x816e,		/* ] */
	0x814f,		/* ^ */
	0x8151,		/* _ */
	0x8165,		/* ` */
	0x816f,		/* { */
	0x8162,		/* | */
	0x8170,		/* } */
	0x8150,		/* ~ */
};


void ClearCardsPresent()
{
	int i,j;

	for(i = 0;i < 4;i++)
	{
		for(j = 0;j < 2;j++)
		{
			vmuStatus[i][j] = -1;
			vmuSpace[i][j] = -1;
			vmuGame[i][j] = 0;
		}
	}
}

void cardFindMenuSfx(int set)
{
	//this does something on the psx!
	cardmenusfxset = set;
	if(!set)
	{
		// Generic (inventory) samples
		cardmenusfx1 = sfxFindSampleInAllBanks("bleep4");
		cardmenusfx2 = sfxFindSampleInAllBanks("clik4");
		cardmenusfx3 = sfxFindSampleInAllBanks("clik3");

		cardmenusfx_pitch1 = 800;
		cardmenusfx_pitch2 = 800;
		cardmenusfx_pitch3 = 800;
		cardmenusfx_vol1 = 0x8888;
		cardmenusfx_vol2 = 0x8888;
		cardmenusfx_vol3 = 0xffff;
		DB("SFX (Generic Set) FOUND\n");
	}
	else
	{
		// MainMenu samples
		cardmenusfx1 = sfxFindSampleInAllBanks("clik1");	// go
		cardmenusfx2 = sfxFindSampleInAllBanks("clik1");	// cancl
		cardmenusfx3 = sfxFindSampleInAllBanks("clik2");	// sel
		cardmenusfx_pitch1 = 1000;
		cardmenusfx_pitch2 = 1000;
		cardmenusfx_pitch3 = 1000;
		cardmenusfx_vol1 = 0xffff;
		cardmenusfx_vol2 = 0xffff;
		cardmenusfx_vol3 = 0xffff;
		DB("SFX (Menu Set) FOUND\n");
	}
	ASSERT (cardmenusfx1 && cardmenusfx2 && cardmenusfx3);
}


// Convert the Ascii Text String to SJIS.
void asciiStringToSJIS(unsigned char *string, unsigned char *dest) 
{
	int	i;
	
	int sjis_code;
	int ascii_code;
	
	unsigned char stmp = 0;
	unsigned char stmp2;

	unsigned char *dest2;
						   
	dest2 = dest;

	for( i=0; i<32; i++ )
	{
		*dest2++ = 0x81;				  
		*dest2++ = 0x40;
	}
	
							    
	while(*string)
	{
		stmp2 = 0;
		ascii_code = *string++;
		if ((ascii_code >= 0x20) && (ascii_code <= 0x2f))
			stmp2 = 1;
		else
		if ((ascii_code >= 0x30) && (ascii_code <= 0x39))
			stmp = 0;
		else
		if ((ascii_code >= 0x3a) && (ascii_code <= 0x40))
			stmp2 = 11;
		else
		if ((ascii_code >= 0x41) && (ascii_code <= 0x5a))
			stmp = 1;
		else
		if ((ascii_code >= 0x5b) && (ascii_code <= 0x60))
			stmp2 = 37;
		else
		if ((ascii_code >= 0x61) && (ascii_code <= 0x7a))
			stmp = 2;
		else
		if ((ascii_code >= 0x7b) && (ascii_code <= 0x7e))
			stmp2 = 63;
		else {
			printf("bad ASCII code 0x%x\n", ascii_code);
			break;
			//exit(1);
		}

		if(stmp2)
			sjis_code = ascii_k_table[ascii_code - 0x20 - (stmp2 - 1)];
		else
			sjis_code = ascii_table[stmp][0] + ascii_code - ascii_table[stmp][1];

		/* Write sjis */
		*dest++ = (sjis_code&0xff00)>>8;
		*dest++ = (sjis_code&0xff);
	}
}


// ===================================================================
// Routines to extract / fill in the entire save block

// addr points to a MAX_SAVE_SIZE-byte malloc to fill in
void cardBuildBlock(CARD_SAVE_STR *data)
{

// Header first...
	memset(data,0,MAX_SAVE_SIZE);
	{
//		NSPRITE *spr;

//		sound_XA_PauseForLoading();
/*
		spr = (NSPRITE *)fileLoad("SHELL\\SAVEICON.SPT",0);
		sound_XA_Resume(-1);

		if(spr)
		{
// NB - The card data headers are not quad aligned
			memcpy(&data->header.icons[0],ADD2POINTER(spr,(int)spr->image + 4), 128);
			memcpy(&data->header.icons[1],ADD2POINTER(spr,(int)spr->image + 4), 128);	// only got the one image at the mo
			memcpy(&data->header.icons[2],ADD2POINTER(spr,(int)spr->image + 4), 128);
			memcpy(&data->header.clut,ADD2POINTER(spr,(int)spr->pal), 32);
		}
		FREE(spr);
*/
/*
		ULONG *icondata;
		icondata=(ULONG *)fileLoad("SHELL\\SAVE.BIN",0);
		if(icondata)
		{
			memcpy(&data->header,icondata,512);
			FREE(icondata);
		}
*/
	}
	data->header.magic[0] = 'S';								// Sony header format
	data->header.magic[1] = 'C';
#if PALMODE==1
	data->header.type = 0x11;
#else
	data->header.type = 0x13;
#endif
	data->header.blockEntry = 1;
	asciiStringToSJIS(cardOURTITLE, &data->header.title[0]);

	data->version = CARD_SAVE_VERSION;
	data->size = sizeof(CARD_SAVE_STR);

	gameSaveData.pad[0] = palMode;

	gameSaveData.current_lev = options_returnlevel;

	memcpy(&data->options,&optionData, sizeof(OPTIONDATASTR));
	memcpy(&data->state,&gameSaveData,sizeof(GameSaveDataType));
	memcpy(&data->play,&gamePlayDataMain,sizeof(GamePlayDataType));


}

// ===================================================================
// addr points to a MAX_SAVE_SIZE-byte area that's just been read in
void cardExtractGameState(CARD_SAVE_STR *data)
{
	memcpy(&gameSaveData,&data->state,sizeof(GameSaveDataType));

	if((palMode) && (gameSaveData.pad[0] == 0))
	{
		gameSaveData.best_times[TIME_DOGCHASE] *= 25.0/30.0;
		gameSaveData.best_times[TIME_COGCHASE] *= 25.0/30.0;
		gameSaveData.best_times[TIME_ACT1] *= 25.0/30.0;
		gameSaveData.best_times[TIME_ACT2] *= 25.0/30.0;
		gameSaveData.best_times[TIME_ACT4] *= 25.0/30.0;

		gameSaveData.time_spent_playing *= 25.0/30.0;
	}
	else if((palMode == 0) && (gameSaveData.pad[0]))
	{
		gameSaveData.best_times[TIME_DOGCHASE] *= 30.0/25.0;
		gameSaveData.best_times[TIME_COGCHASE] *= 30.0/25.0;
		gameSaveData.best_times[TIME_ACT1] *= 30.0/25.0;
		gameSaveData.best_times[TIME_ACT2] *= 30.0/25.0;
		gameSaveData.best_times[TIME_ACT4] *= 30.0/25.0;

		gameSaveData.time_spent_playing *= 30.0/25.0;
	}

	memcpy(&gamePlayDataMain,&data->play,sizeof(GamePlayDataType));
	levflowSortOutFowlersDoor();
}

// ** But Not The Language **
void cardExtractPrefs(CARD_SAVE_STR *data)
{
	memcpy(&optionData, &data->options, sizeof(OPTIONDATASTR));
	if(soundMode == SYD_CFG_STEREO)
		optionData.sound.stereo = TRUE;
	else
		optionData.sound.stereo = FALSE;
	optionsHitOptions(OPTIONS_HIT_ALL);
}


// initialise anything that's non-zero
void cardInitialGameState()	//GameSaveDataType *data)
{
	int i;
	GameSaveDataType *data = &gameSaveData;

#ifdef DEBUG
	if(sizeof(CARD_SAVE_STR) > MAX_SAVE_SIZE)
	{
		DB("card save str is %d. Too big - should be <= %d\n",sizeof(CARD_SAVE_STR),MAX_SAVE_SIZE);
	}
	ASSERT(sizeof(CARD_SAVE_STR) <= MAX_SAVE_SIZE);
#endif

	FMEMZERO(data,sizeof(GameSaveDataType));
	FMEMZERO(gamePlayData,sizeof(GamePlayDataType));

	for(i = 0; i < NUM_CHARACTER_INVS; i++)
	{
		currentInv = &gamePlayData->inventories[i];
		inventoryReset();
	}

	gamePlayData->map_pieces_got[8] = 1;	// Always got the first map piece
	gameSaveData.movies_available[0]=1;	// RFW - Make the game intro available

	gameSaveData.levels_available[REPLAY_AVAILABLE_ACT1] = REPLAY_NOTBEATEN;

	gameSaveData.best_times[TIME_DOGCHASE] = INIT_DOGCHASE_BEST;		// Lets make it 5 minutes...
	gameSaveData.best_times[TIME_COGCHASE] = INIT_COGCHASE_BEST;		// Lets make it 5 minutes...
	gameSaveData.best_times[TIME_ACT1] = INIT_ACT1_BEST;		// Lets make it 30 minutes...
	gameSaveData.best_times[TIME_ACT2] = INIT_ACT2_BEST;		// Lets make it 90 minutes...
	gameSaveData.best_times[TIME_ACT4] = INIT_ACT3_BEST;		// Lets make it 120 minutes...

	gameSaveData.current_lev = options_returnlevel = LEVNUMBER_A1_HUT17;

	currentInv = &gamePlayData->inventories[INVNO_GINGER];

#ifdef GAME_SHOW_DEMO
	for(i = 0; i < N_REPLAY_LEVELS; i++)
	{
		//This makes the door go to a1_coop1_n, but everything is available thru the bomb
		gameSaveData.levels_available[i] = REPLAY_NOTBEATEN;
	}
#endif

}

void cardInitialPrefs(OPTIONDATASTR *data)
{
	FMEMZERO(data,sizeof(OPTIONDATASTR));
	optionsResetAllToDefaults();

	optionsHitOptions(OPTIONS_HIT_ALL);	// actually called by "resetalltodefaults", but better safe than sorry

}

// ================================================================
// The bootup load is done before we know what language we're in.
// Consequently, we can't put up an "accessing" screen

#define CARD_FROM_LANGUAGE	0
#define CARD_FROM_FOWLERS	1
#define CARD_FROM_SPLASH	2

typedef struct CARDSTR
{
	char from;
	char doing;
	char other_territory;
	int ticker;
	int message;
	int yesno;
}CARDSTR;
CARDSTR cardState;

enum
{
	CARD_POSTLANG_INIT,
	CARD_POSTLANG_LOADING,
	CARD_POSTLANG_EXIT,

	CARD_HUTLOAD_INIT,
	CARD_HUTLOAD_CONFIRMING,
	CARD_HUTLOAD_LOADING,
	CARD_HUTLOAD_EXIT,

	CARD_HUTLOAD_OKAY,
	CARD_HUTLOAD_NONEFOUND,
	CARD_HUTLOAD_NOCARD,
	CARD_HUTLOAD_FAILED,
	CARD_HUTLOAD_UNFORMATTED,

	CARD_HUTSAVE_INIT,
	CARD_HUTSAVE_CONFIRMING,
	CARD_HUTSAVE_PRESAVE,
	CARD_HUTSAVE_SAVING,
	CARD_HUTSAVE_FORMAT_YN,
	CARD_HUTSAVE_FORMATTING,
	CARD_HUTSAVE_EXIT
};


//#define CARD_HEADING_Y -60
//#define CARD_MESSAGE_Y -20
//#define CARD_MESSAGE_Y -65

int card_message_y = -65;
int card_menu_y = -65 + 62 + 60;

// ================================================================

// Allow loading from either territory's save game

// Check size of data matches the expected size, and report a corrupt save rather than a non-present one
// for testers using old savegames

int cardCheckRead(char *name, CARD_SAVE_STR *data, int size)
{
	int rval;

	utilPrintf("Card Check Read....\n");
	cardState.other_territory = 0;
	rval = cardRead(name,data,size);

	if((rval == CARDREAD_OK) && (data))
	{
		if(data->version != CARD_SAVE_VERSION || data->size != sizeof(CARD_SAVE_STR))
		{
			overlayDebugMessage("Old Version Save File. Sorry", 60);
			utilPrintf("Old-version save file. Claiming Corruption\n");
			utilPrintf("rqd vsn,len = %d %d. Actual vsn,len = %d %d\n",
				CARD_SAVE_VERSION,sizeof(CARD_SAVE_STR),data->version, data->size);
			return(CARDREAD_CORRUPT);
		}
	}
// Takes too long with a 15-item card
/*
	if(rval == CARDREAD_NOTFOUND || rval ==  CARDREAD_NOTFOUNDANDFULL)
	{
		rval = cardRead(OTHERMEMORYCARDNAME,data,MAX_SAVE_SIZE);
		if(rval == CARDREAD_OK)
		{
			utilPrintf("Other Territory\n");
			if(data->version != CARD_SAVE_VERSION || data->size != sizeof(CARD_SAVE_STR))
			{
				utilPrintf("Old-version save file. Claiming Corruption\n");
				return(CARDREAD_CORRUPT);
			}
			cardState.other_territory = 1;
		}
	}
*/
	return rval;
}

// ================================================================
// Initial preferences load
// On Action Man, this comes AFTER the "language flags" screen, and is actually part of the
// "intro cutscene" playing code...
// Basically, it loads the prefs, and whinges if there's not enough room on the memory card
// It *times out*, and doesn't deal with "unformatted" issues
#define POSTLANG_OK_DELAY		20
#define POSTLANG_IGNORE_DELAY	30
#define POSTLANG_WARNING_DELAY	150
/*
void cardPrintMessage()
{

	options_colour+=options_colouradd;
	if((options_colour<0) ||(options_colour>64))
		options_colouradd=-options_colouradd;

	if(cardState.message)
	{
		fontCentredMultiLine(font, 0, card_message_y, 400, TEXTSTR(cardState.message), 128,128,128);
	}
	switch(cardState.message)
	{
		case TEXTSTR_SAVE_ACCESSING:
		case TEXTSTR_SAVE_LOADING:
		case TEXTSTR_SAVE_SAVING:
		case TEXTSTR_SAVE_FORMATTING:
			fontCentredMultiLine(font, 0, card_message_y + 12, 400, TEXTSTR(TEXTSTR_SAVE_DONTREMOVE), 128,128,128);
			break;
		case TEXTSTR_SAVE_CONFIRM_SAVE:

			if(cardState.yesno)
			{
				fontPrintCentred(font,0,card_message_y + 12,TEXTSTR(TEXTSTR_YES),64+options_colour,64+options_colour,64+options_colour);
				fontPrintCentred(font,0,card_message_y + 24,TEXTSTR(TEXTSTR_NO),48,48,48);
			}
			else
			{
				fontPrintCentred(font,0,card_message_y + 12,TEXTSTR(TEXTSTR_YES),48,48,48);
				fontPrintCentred(font,0,card_message_y + 24,TEXTSTR(TEXTSTR_NO),64+options_colour,64+options_colour,64+options_colour);
			}
			break;
	}
}
*/

void cardPostLangDraw()
{
//	startupDraw();
//	languageDrawBackground();


// Note - the backdrop needs to be black when we hit the screen position
	int alpha;
	unsigned int plank_colour;
	int i, j;

/*	if(cardState.doing == CARD_POSTLANG_INIT)
	{
		// damp the language planks down to black...
	//	alpha = -cardState.ticker * 127 / 10;
	//	plank_colour = alpha * 0x02010101;
//		utilPrintf("tick=%d, alpha = %d, plankcol &%8x\n",cardState.ticker,alpha,plank_colour);
	}
	else if(cardState.doing == CARD_POSTLANG_EXIT)
	{
		// fade the planks back in again after setting the screen position
		alpha = (cardState.ticker - (POSTLANG_WARNING_DELAY-POSTLANG_OK_DELAY)) * 127 / POSTLANG_OK_DELAY;
		plank_colour = 0;//alpha * 0x02010101;
	}
	else
	{
		plank_colour = 0;
	}*/

	for(i=-256;i<256;i+=64)
	{
		for(j=-127;j<120;j+=64)
		{
			//alpha * 
			overlayAlphaSprite(plank, i, j, 1023, 0x808080);//plank_colour);
		}
	}

//	cardPrintMessage();
	if(cardState.message)
	{
		fontCentredMultiLine(font, 0, card_message_y, 400, TEXTSTR(cardState.message), 128,128,128);
	}
}

void cardPostLangExit()
{
	startupContinue();
	ShowLCDLogo();
}


void cardPostLangCycle()
{
	CARD_SAVE_STR *data;
	int rval;

	if(cardState.ticker < 0)	// Allow a little "wait for the display to flip to what we're talking about" delay
	{
		cardState.ticker++;
		return;
	}
	if(cardState.ticker < 1000)
		cardState.ticker++;

	switch(cardState.doing)
	{
	case CARD_POSTLANG_INIT:
		cardState.message = TEXTSTR_MCARD_PREFS_ACCESSING;
		cardState.doing = CARD_POSTLANG_LOADING;
		cardState.ticker = -2;
		break;

	case CARD_POSTLANG_LOADING:
		data = MALLOC(MAX_SAVE_SIZE);

		rval = CheckVMUs(NULL);//cardCheckRead(MEMORYCARDNAME,data,MAX_SAVE_SIZE);
		cardState.ticker = POSTLANG_WARNING_DELAY-POSTLANG_OK_DELAY;

		switch(rval)
		{
// ** The bootup load is just THE OPTIONS **
			case CARDREAD_OK:
				cardState.ticker = POSTLANG_WARNING_DELAY-POSTLANG_OK_DELAY;
				cardState.message = 0;
				cardInitialGameState(data);
				cardExtractPrefs(data);
				break;


// This is a happy "do nowt" routine
			case CARDREAD_NOTFOUND:
				cardState.ticker = POSTLANG_WARNING_DELAY-POSTLANG_OK_DELAY;
				cardState.message = 0;
				break;


// Action man didn't do this one, but it might be worth dealing with...
// (Warning that you won't be able to save without a memory card. Like, duh!)
			case CARDREAD_NOCARD:
				cardState.ticker = POSTLANG_WARNING_DELAY-POSTLANG_IGNORE_DELAY;
				cardState.message = 0;
				break;


// This is the one that we *NEED* to deal with here...
// (Print up a warning that you won't be able to save without freeing some space)
			case CARDREAD_NOTFOUNDANDFULL:
				cardState.message = TEXTSTR_MCARD_PREFS_FULL;
				cardState.ticker = 0;	// delay for the full warning delay
				break;


// AM didn't deal with these on the post-language memory-card check, so I won't either
			default:
			case CARDREAD_CORRUPT:
			case CARDREAD_BADCARD:
			case CARDREAD_NOTFORMATTED:
				cardState.message = 0;
				cardState.ticker = POSTLANG_WARNING_DELAY-POSTLANG_IGNORE_DELAY;
				break;

		}
		FREE(data);
		cardState.doing = CARD_POSTLANG_EXIT;
		break;


	case CARD_POSTLANG_EXIT:
		if(padData.debounce[0] & (PAD_CROSS + PAD_CIRCLE + PAD_START + PAD_SELECT))
		{
			if(cardState.ticker < POSTLANG_WARNING_DELAY - POSTLANG_OK_DELAY)
			{
				cardState.ticker = POSTLANG_WARNING_DELAY - POSTLANG_OK_DELAY;
			}
		}

		if(cardState.ticker >= POSTLANG_WARNING_DELAY)
		{
			cardPostLangExit();
		}
		break;
	}

}

void cardPostLangStart()
{
	draw_routine = cardPostLangDraw;
	cycle_routine = cardPostLangCycle;

	cardState.from = CARD_FROM_LANGUAGE;
	card_message_y = -65;
	card_menu_y = -65 + 62 + 60;
	cardState.doing = CARD_POSTLANG_INIT;
	cardState.ticker = -10;
	cardState.message = 0;
	cardState.other_territory = 0;
}


// ================================================================
// Okay... Now we need to do the fowler's hut stuff
// Where you can LOAD a game
// (which can be from a different memory card from that which you booted up with)
// and SAVE a game, too. Goody!
// tbd - "Load & Save in the Pie Machine Act" problems are still to deal with
// ================================================================

#define HUTLOAD_OK_DELAY		20
#define HUTLOAD_IGNORE_DELAY	30
#define HUTLOAD_WARNING_DELAY	150

void cardGameLoadDraw()
{
	char mess[256];

	optionsDrawOnly();
	optionsAlphaRectangle(-200, -70, 400, loadSaveNumLines*12 + 10, 0, 0x202020);//0x001020);
	optionsAlphaRectangle(-200, card_menu_y - 5, 400, (2*20)+5, 0, 0x202020);//0x001020);
//	fontPrintCentred(font,0,CARD_HEADING_Y, TEXTSTR(TEXTSTR_SAVE_LOAD),128,128,128);

//	if(cardState.message)
//		cardPrintMessage();

// Some parts of "load" are still using the old message setter, for convenience.
// (Actually, only the "accessing..." is still in)
	if(cardState.message)
	{
		if(gameTextLang == LANG_D)
			sprintf(mess,TEXTSTR(cardState.message),selectedVMU + 1,portName[selectedPad]);
		else
			sprintf(mess,TEXTSTR(cardState.message),portName[selectedPad],selectedVMU + 1);

		loadSaveNumLines = fontCentredMultiLine(font, 0, card_message_y, 400, mess, 128,128,128);
	}

}

void cardGameLoadExit()
{
	if(cardState.from == CARD_FROM_FOWLERS)
	{
		DB("Options Return\n");
		optionsReturn();
	}
	else
	{
		DB("Splash Return\n");
		levflowSplashSaveReturn();
	}
	ShowLCDLogo();
}



// ======== AM2 equivalents for the load-file stuff ===========
void loadMenuLoadYN()
{
	cardState.message = NULL;
	switch(streetChooseOption(
		TEXTSTR(TEXTSTR_MCARD_LOADYN),
		TEXTSTR(TEXTSTR_MCARD_LOADGAME),
		TEXTSTR(TEXTSTR_MCARD_CANCELLOAD)
		))
	{
	case 3:
//			street.saveStage = SAVEMENU_CHECK;
//			streetStartChooseOption();
		cardState.doing = CARD_HUTLOAD_LOADING;
		cardState.message = TEXTSTR_MCARD_CHECK;
		cardState.ticker = -2;
		break;

	case 2:
//			street.saveFrame = 0;
		cardState.doing = CARD_HUTLOAD_EXIT;
		break;
	}
}

void loadMenuComplete()
{
	if (streetChooseOption(
		TEXTSTR(TEXTSTR_SAVE_LOADEDOK),	// oops. not "mcard" in the list
		TEXTSTR(TEXTSTR_MCARD_CONTINUE),
		NULL))
	{
		cardState.doing = CARD_HUTLOAD_EXIT;
	}
}

void loadMenuFail()
{
	cardState.message = NULL;
	switch (streetChooseOption(
		TEXTSTR(TEXTSTR_MCARD_LOADERROR),
		TEXTSTR(TEXTSTR_MCARD_RETRYLOAD),
		TEXTSTR(TEXTSTR_MCARD_CANCELLOAD)
			))
// Changed to CANCEL rather than infy looping. RFW
//		NULL))
	{
	case 3:
		cardState.doing = CARD_HUTLOAD_LOADING;
		streetStartChooseOption();
		break;
	case 2:
		cardState.doing = CARD_HUTLOAD_EXIT;
		break;
	}

	switch(cardPresent())
	{
   	case CARDREAD_NOCARD:
		cardState.doing = CARD_HUTLOAD_NOCARD;
		streetStartChooseOption();
		break;
	}
}


void loadMenuUnformatted()
{
	cardState.message = NULL;
	switch (streetChooseOption(
		TEXTSTR(TEXTSTR_MCARD_UNFORMAT_WARNING),	// JUST A WARNING. You don't get a "format?" option on load.
		TEXTSTR(TEXTSTR_MCARD_RETRYLOAD),
		TEXTSTR(TEXTSTR_MCARD_CANCELLOAD)
			))
// Changed to CANCEL rather than infy looping. RFW
//		NULL))
	{
	case 3:
		cardState.doing = CARD_HUTLOAD_LOADING;
		streetStartChooseOption();
		break;
	case 2:
		cardState.doing = CARD_HUTLOAD_EXIT;
		break;
	}

	switch(cardPresent())
	{
   	case CARDREAD_NOCARD:
		cardState.doing = CARD_HUTLOAD_NOCARD;
		streetStartChooseOption();
		break;
	}
}



void loadMenuNoCard()
{
	switch(streetChooseOption(
		TEXTSTR(TEXTSTR_MCARD_NOCARD_LOAD),
		TEXTSTR(TEXTSTR_MCARD_LOADGAME),
		TEXTSTR(TEXTSTR_MCARD_CANCELLOAD)
		))
	{
	case 3:
		cardState.doing = CARD_HUTLOAD_LOADING;
		streetStartChooseOption();
		break;
	case 2:
		cardState.doing = CARD_HUTLOAD_EXIT;
		break;
	}
	switch(gameSaveGetCardStatus())
	{
   	case McErrNone:
   	case McErrNewCard:
		cardState.doing = CARD_HUTLOAD_CONFIRMING;
		streetStartChooseOption();
		break;
	}
}

void loadMenuNoneFound()
{
	switch(streetChooseOption(
		TEXTSTR(TEXTSTR_MCARD_NONEFOUND),
		TEXTSTR(TEXTSTR_MCARD_RETRYLOAD),
		TEXTSTR(TEXTSTR_MCARD_CANCELLOAD)
		))
	{
	case 3:
		cardState.doing = CARD_HUTLOAD_LOADING;
		streetStartChooseOption();
		break;
	case 2:
		cardState.doing = CARD_HUTLOAD_EXIT;
		break;
	}
	switch(cardPresent())
	{
   	case CARDREAD_NOCARD:
		cardState.doing = CARD_HUTLOAD_NOCARD;
		streetStartChooseOption();
		break;
	}
}


void cardGameLoadCycle()
{
	CARD_SAVE_STR *data;
	int rval;

	if(cardState.ticker < 0)	// Allow a little "wait for the display to flip to what we're talking about" delay
	{
		cardState.ticker++;
		return;
	}
	if(cardState.ticker < 1000)
		cardState.ticker++;

	switch(cardState.doing)
	{
	case CARD_HUTLOAD_INIT:
		cardState.message = NULL;
		cardState.doing = CARD_HUTLOAD_CONFIRMING;
		cardState.ticker = -2;
		streetStartChooseOption();
		break;

	case CARD_HUTLOAD_CONFIRMING:
		loadMenuLoadYN();
		break;

	case CARD_HUTLOAD_LOADING:
		data = MALLOC(MAX_SAVE_SIZE);
		utilPrintf("Card Read....\n");

		rval = cardCheckRead(MEMORYCARDNAME,data,MAX_SAVE_SIZE);
		cardState.ticker = HUTLOAD_WARNING_DELAY-HUTLOAD_OK_DELAY;
		cardState.doing = CARD_HUTLOAD_OKAY;

		cardState.message = NULL;
		switch(rval)
		{
			case CARDREAD_OK:
//				cardState.message = TEXTSTR_SAVE_LOADEDOK;
				cardExtractPrefs(data);
				cardExtractGameState(data);
				cardState.doing = CARD_HUTLOAD_OKAY;
				ASSERT(gameSaveData.current_lev);
				options_returnlevel = gameSaveData.current_lev;
				break;

			case CARDREAD_NOTFOUND:
//				cardState.message = TEXTSTR_SAVE_NONEFOUND;
				cardState.doing = CARD_HUTLOAD_NONEFOUND;
				break;
			case CARDREAD_NOTFOUNDANDFULL:
//				cardState.message = TEXTSTR_SAVE_NONEFOUND;
				cardState.doing = CARD_HUTLOAD_NONEFOUND;
				break;

			case CARDREAD_NOCARD:	// loops internally
//				cardState.message = TEXTSTR_SAVE_CARD_FOR_LOAD;
				cardState.doing = CARD_HUTLOAD_NOCARD;
				break;

			case CARDREAD_BADCARD:
				cardState.ticker = 0;
//				cardState.message = TEXTSTR_SAVE_FAILEDLOAD;
				cardState.doing = CARD_HUTLOAD_FAILED;
				break;

			case CARDREAD_NOTFORMATTED:
				cardState.ticker = -2;
// TRC text: During the load process,
// do not format the MEMORY CARD if it is unformatted
// or perform any other maintenance on it.				

// (There's not much point in formatting a card before loading from it, because, umm, a newly formatted
// card won't have a savegame on it, now will it?)

//				cardState.message = TEXTSTR_SAVE_UNFORMATTED_OLD;
				cardState.doing = CARD_HUTLOAD_UNFORMATTED;
				break;

			case CARDREAD_CORRUPT:
			default:
				cardState.ticker = 0;
//				cardState.message = TEXTSTR_SAVE_FAILEDLOAD;
				cardState.doing = CARD_HUTLOAD_FAILED;
				break;
		
		}
		FREE(data);
		break;

	case CARD_HUTLOAD_NOCARD:
		loadMenuNoCard();
		break;

	case CARD_HUTLOAD_NONEFOUND:
		loadMenuNoneFound();
		break;

	case CARD_HUTLOAD_UNFORMATTED:
		loadMenuUnformatted();	// JUST A WARNING on load. You only get the "format?" option on save
		break;

	case CARD_HUTLOAD_FAILED:
		loadMenuFail();
		break;

	case CARD_HUTLOAD_OKAY:
		loadMenuComplete();
		break;
	case CARD_HUTLOAD_EXIT:
		cardGameLoadExit();
		break;

	}
}

void cardFowlerLoadStart()
{
	gotoLoad = YES;

	draw_routine = selectVMUDraw;
	cycle_routine = selectVMUCycle;

	ClearCardsPresent();

	utilPrintf("LOAD START\n");

	cardState.from = CARD_FROM_FOWLERS;
	card_message_y = -65;
	card_menu_y = -65 + 62 + 60;
	cardState.doing = CARD_HUTLOAD_INIT;
	cardState.ticker = 0;
	cardState.message = 0;

}

// ================================================================
// Action-Man-2 cloning in progress....
// (stmenu.c)

//static int framePause;
static int	optChosen, optFrame, optSaveAlready, optSaveSlot;

void streetSimpleMessage(char *msg, uchar rgb)
{
	char mess[256];

	if(gameTextLang == LANG_D)
		sprintf(mess,msg,selectedVMU + 1,portName[selectedPad]);
	else
		sprintf(mess,msg,portName[selectedPad],selectedVMU + 1);

	loadSaveNumLines = fontCentredMultiLine(font,0,card_message_y,380,mess,128,128,128);
}

// Note - Different from the AM2 one.
// AM2 has "CIRCLE = this", "CROSS = the other", and consequently
// it's either NEITHER chosen, or FADING OUT everything bar the chosen thing.
// (ie, There's no currently chosen/unchosen, but non-active menu thing in AM2)
int streetChooseOption(char *msg, char *msg1, char *msg2)
{
	char mess[256];

	options_colour+=options_colouradd;
	if((options_colour<0) ||(options_colour>64))
		options_colouradd=-options_colouradd;

	if(gameTextLang == LANG_D)
		sprintf(mess,msg,selectedVMU + 1,portName[selectedPad]);
	else
		sprintf(mess,msg,portName[selectedPad],selectedVMU + 1);

	loadSaveNumLines = fontCentredMultiLine(font,0,card_message_y,400,mess,128,128,128);

	if(!msg2)
		optChosen = 3;

	switch(optChosen)
	{
	case 3:
		fontPrintCentred(font,0,card_menu_y,msg1,64+options_colour,64+options_colour,64+options_colour);
		if(msg2)
			fontPrintCentred(font,0,card_menu_y + 20,msg2,48,48,48);
		break;
	case 2:
		fontPrintCentred(font,0,card_menu_y,msg1,48,48,48);
		fontPrintCentred(font,0,card_menu_y + 20,msg2,64+options_colour,64+options_colour,64+options_colour);
		break;
	default:
		optChosen = 3;
		break;
	}

	if(msg2)
	{
		if(padData.debounce[0] & PAD_UP || padData.debounce[0] & PAD_DOWN)
		{
			optChosen = ((optChosen + 1) & 1) + 2;
			soundPlaySample(sfxFindSampleInAllBanks("clik2"),0xffff,1000);
		}
	}

	if(padData.debounce[0] & PAD_CROSS)
	{
		soundPlaySample(sfxFindSampleInAllBanks("bleepselect"),0xffff,1000);
		return optChosen;
	}
	else
		return 0;
}


// Heh. Well, it means that the source can be the same...
typedef struct {
	int saveFrame;
	int saveChosen;
	int saveStage;
}AM2_STREETSTR;

AM2_STREETSTR street;

/*
enum {
	SAVEMENU_SAVEYN,
	SAVEMENU_CHECK,
	SAVEMENU_FULL,
	SAVEMENU_NOCARD,
	SAVEMENU_SLOT,
	SAVEMENU_OVERWRITE,
	SAVEMENU_SAVE,
	SAVEMENU_COMPLETE,
	SAVEMENU_FAIL,
	SAVEMENU_FORMATYN,
	SAVEMENU_FORMAT,
	SAVEMENU_FORMATFAIL,
};
*/

void streetStartChooseOption()
{
//	framePause++;
	optFrame = 1;
	optChosen = 0;
}

void streetStartChooseSave()
{
	street.saveFrame = 1;
	street.saveChosen = 0;
	street.saveStage = SAVEMENU_SAVEYN;
//	framePause++;
	streetStartChooseOption();
}


/*
int gameSaveGetCardStatus()
{
	long	cmds, result;
	//	i'm guessing this was commented out in the DC version for a reason - who knows...
	//switch(MemCardSync(1, &cmds, &result))
	//{
	//case -1:	// Start off status request
	//	MemCardExist(0);
	//	break;
	//case 1:		// Return status result
	//	if (cmds==McFuncExist)
	//		return result;
	//	break;
	//} 
	return -1;
}
*/
/* SAVE CONFIRM PAGE ***************************************************************************************************/

static void saveMenuSaveYN()
{
	switch(streetChooseOption(
		TEXTSTR(TEXTSTR_MCARD_SAVEYN),
		TEXTSTR(TEXTSTR_MCARD_SAVEGAME),
		TEXTSTR(TEXTSTR_MCARD_NOSAVE)))
	{
	case 3:
		street.saveStage = SAVEMENU_CHECK;
		streetStartChooseOption();
		break;
	case 2:
		street.saveFrame = 0;
		break;
	}
	switch(cardPresent())
	{
   	case CARDREAD_NOCARD:
		street.saveStage = SAVEMENU_NOCARD;
		streetStartChooseOption();
		break;
	}
}

/* FULL CARD PAGE ******************************************************************************************************/

static void saveMenuFull()
{
	switch(streetChooseOption(
		TEXTSTR(TEXTSTR_MCARD_FULL),
		TEXTSTR(TEXTSTR_MCARD_RETRYSAVE),
		TEXTSTR(TEXTSTR_MCARD_CANCELSAVE)
		))
	{
	case 3:
		street.saveStage = SAVEMENU_CHECK;
		streetStartChooseOption();
		break;
	case 2:
		street.saveFrame = 0;
		break;
	}
	switch(cardPresent())
	{
   	case CARDREAD_NOCARD:
		street.saveStage = SAVEMENU_NOCARD;
		streetStartChooseOption();
		break;
	}
}

/* NO CARD PAGE ********************************************************************************************************/

static void saveMenuNoCard()
{
	switch(streetChooseOption(
		TEXTSTR(TEXTSTR_MCARD_NOCARD_SAVE),
		TEXTSTR(TEXTSTR_MCARD_SAVEGAME),
		TEXTSTR(TEXTSTR_MCARD_CANCELSAVE)
		))
	{
	case 3:
		street.saveStage = SAVEMENU_CHECK;
		streetStartChooseOption();
		break;
	case 2:
		street.saveFrame = 0;
		break;
	}
	switch(gameSaveGetCardStatus())
	{
   	case McErrNone:
   	case McErrNewCard:
// RFW - Changed coz we've not got a SLOT selection, and "CHECK" goes straight to SAVING
//		street.saveStage = SAVEMENU_CHECK;
		street.saveStage = SAVEMENU_SAVEYN;
		streetStartChooseOption();
		break;
	}
}

/* CHECK CARD PAGE *****************************************************************************************************/

// This is only here to check that there's a savegame on the card

/*
int gameSaveHandleLoad(int i)
{
	CARD_SAVE_STR *data;
	int rval;

	data = MALLOC(MAX_SAVE_SIZE);
	rval = cardCheckRead(MEMORYCARDNAME,data,MAX_SAVE_SIZE);
	FREE(data);
	return rval;

}
*/
int ignoreAbort = NO;
int gameSaveHandleSave()
{
	CARD_SAVE_STR *data;
	int rval;

	ignoreAbort = YES;

	data = MALLOC(MAX_SAVE_SIZE);
	cardBuildBlock(data);
	utilPrintf("Card Write....\n");
	rval = cardWrite(MEMORYCARDNAME,data,MAX_SAVE_SIZE);
	FREE(data);

	ignoreAbort = NO;
	if(globalAbortFlag == 0)
	{
		gdFsReqDrvStat();
		if(gdFsGetDrvStat() == GDD_DRVSTAT_OPEN)
			globalAbortFlag = 1;
	}
	return rval;

}

static void saveMenuCheck()
{
	drawLCD = NO;
	streetSimpleMessage(TEXTSTR(TEXTSTR_MCARD_CHECK), MIN(100,optFrame*30));
	if (optFrame++>4)
	{
		switch(gameSaveHandleLoad(1))
		{
		case 0:						// Loaded fine
			optSaveAlready = 1;
//			if (!arcadeMode)
//				street.saveStage = SAVEMENU_SLOT;
//			else
				street.saveStage = SAVEMENU_OVERWRITE;
			streetStartChooseOption();
			break;
		case -1:					// Data not found, space on card
		case -2:					// Corrupt data
		case McErrFileNotExist:
			optSaveAlready = 0;
//			if (!arcadeMode)
//				street.saveStage = SAVEMENU_SLOT;
//			else
				street.saveStage = SAVEMENU_SAVE;
			streetStartChooseOption();
			break;
   		case McErrCardNotExist:		// No card
   		case McErrCardInvalid:		// Bad card
			street.saveStage = SAVEMENU_NOCARD;
			streetStartChooseOption();
			break;
		case -3:					// Data not found, full card
			street.saveStage = SAVEMENU_FULL;
			streetStartChooseOption();
			break;
		case McErrNotFormat: 		// Card not formatted
			street.saveStage = SAVEMENU_FORMATYN;
			streetStartChooseOption();
			break;
		}
	}
	drawLCD = YES;
}

/* SAVE OVERWRITE PAGE *************************************************************************************************/


static void saveMenuOverwrite()
{
	switch(streetChooseOption(TEXTSTR(TEXTSTR_MCARD_OVERWRITE), TEXTSTR(TEXTSTR_MCARD_SAVEGAME), TEXTSTR(TEXTSTR_MCARD_PREVIOUS)))
	{
	case 3:
		street.saveStage = SAVEMENU_SAVE;
		streetStartChooseOption();
		break;
	case 2:
		draw_routine = selectVMUDraw;
		cycle_routine = selectVMUCycle;
		break;
	}
	switch(cardPresent())
	{
   	case CARDREAD_NOCARD:
		street.saveStage = SAVEMENU_NOCARD;
		streetStartChooseOption();
		break;
	}
}

/* SAVE SLOT PAGE ******************************************************************************************************/

static void saveMenuSlot()
{
	street.saveStage = SAVEMENU_SAVE;
// RFW - AM2 has multiple slots in a savegame. We've just got one.
// So... This branches direct to "SAVING"
// AND, anything (eg - no card, then inserting one) which used to rely
// on this second "choice" menu being present, needs to go to the "save y/n" screen
// instead of here.
/*
	saveChooseSlot();
	if (optFrame==0)
	{
		switch(optChosen)
		{
		case 1:
			street.saveFrame = 0;
			break;
		case 2:
		case 3:
		case 4:
			optSaveSlot = optChosen-2;
			if (optSaveAlready)
				street.saveStage = SAVEMENU_OVERWRITE;
			else
				street.saveStage = SAVEMENU_SAVE;
			streetStartChooseOption();
			break;
		}
	}
	switch(gameSaveGetCardStatus())
	{
   	case McErrCardNotExist:
   	case McErrCardInvalid:
		street.saveStage = SAVEMENU_NOCARD;
		streetStartChooseOption();
		break;
	}
*/
}

/* SAVE PAGE ***********************************************************************************************************/

static void saveMenuSave()
{
	int err;

	streetSimpleMessage(TEXTSTR(TEXTSTR_MCARD_SAVING), MIN(100,optFrame*30));
	if (optFrame++>4)
	{
		err = gameSaveHandleSave(optSaveSlot);
		if (err == McErrBlockFull)
			street.saveStage = SAVEMENU_FULL;
		else if(err)
			street.saveStage = SAVEMENU_FAIL;
		else
			street.saveStage = SAVEMENU_COMPLETE;
		streetStartChooseOption();
	}
}

/* SAVE COMPLETE PAGE **************************************************************************************************/

static void saveMenuComplete()
{
	if (streetChooseOption(
		TEXTSTR(TEXTSTR_MCARD_COMPLETE),
		TEXTSTR(TEXTSTR_MCARD_CONTINUE),
		NULL))
		street.saveFrame = 0;
}

/* SAVE FAIL PAGE ******************************************************************************************************/

static void saveMenuFail()
{
	switch (streetChooseOption(
		TEXTSTR(TEXTSTR_MCARD_SAVEERROR),
		TEXTSTR(TEXTSTR_MCARD_RETRYSAVE),
		TEXTSTR(TEXTSTR_MCARD_CANCELSAVE)
			))
// Changed to CANCEL rather than infy looping. RFW
//		NULL))
	{
	case 3:
		street.saveStage = SAVEMENU_CHECK;
		streetStartChooseOption();
		break;
	case 2:
		street.saveFrame = 0;
		break;
	}
/*
	switch(cardPresent())
	{
   	case CARDREAD_NOCARD:
		street.saveStage = SAVEMENU_NOCARD;
		streetStartChooseOption();
		break;
	}
*/
}

/* FORMAT CONFIRMATION PAGE ********************************************************************************************/

static void saveMenuFormatYN()
{
	switch(streetChooseOption(
		TEXTSTR(TEXTSTR_MCARD_UNFORMAT),
		TEXTSTR(TEXTSTR_MCARD_FORMATCARD),
		TEXTSTR(TEXTSTR_MCARD_PREVIOUS)
		))
	{
	case 3:
		street.saveStage = SAVEMENU_FORMAT;
		streetStartChooseOption();
		break;
	case 2:
		street.saveStage = SAVEMENU_CHECK;
		streetStartChooseOption();
		break;
	}
	switch(cardPresent())
	{
   	case CARDREAD_NOCARD:
		street.saveStage = SAVEMENU_NOCARD;
		streetStartChooseOption();
		break;
	}
}

/* FORMAT PAGE *********************************************************************************************************/

static void saveMenuFormat()
{
	streetSimpleMessage(TEXTSTR(TEXTSTR_MCARD_FORMATTING), MIN(100,optFrame*30));
	if (optFrame++>4)
	{
		if (cardFormat())
		{
			street.saveStage = SAVEMENU_FORMATFAIL;
			streetStartChooseOption();
		}
		else
		{
			street.saveStage = SAVEMENU_CHECK;
			streetStartChooseOption();
		}
	}
}

/* FORMAT FAILURE PAGE *************************************************************************************************/

static void saveMenuFormatFail()
{
	switch(streetChooseOption(
		TEXTSTR(TEXTSTR_MCARD_FORMATERROR),
		TEXTSTR(TEXTSTR_MCARD_RETRYSAVE),
		TEXTSTR(TEXTSTR_MCARD_CANCELSAVE)
		))
// Changed to CANCEL rather than infy looping. RFW
//		NULL))
	{
	case 3:
		street.saveStage = SAVEMENU_CHECK;
		streetStartChooseOption();
		break;
	case 2:
		street.saveFrame = 0;
		break;
	}
	switch(cardPresent())
	{
   	case CARDREAD_NOCARD:
		street.saveStage = SAVEMENU_NOCARD;
		streetStartChooseOption();
		break;
	}
}


/**************************************************************************
	FUNCTION:	streetChooseSave
	PURPOSE:	Handle save menu
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

void streetChooseSave()
{
	switch(street.saveStage)
	{
	case SAVEMENU_SAVEYN:
		saveMenuSaveYN();
		break;
	case SAVEMENU_CHECK:
		saveMenuCheck();
		break;
	case SAVEMENU_FULL:
		saveMenuFull();
		break;
	case SAVEMENU_NOCARD:
		saveMenuNoCard();
		break;
	case SAVEMENU_OVERWRITE:
		saveMenuOverwrite();
		break;
//	case SAVEMENU_SLOT:
//		saveMenuSlot();
//		break;
	case SAVEMENU_SAVE:
		saveMenuSave();
		break;
	case SAVEMENU_COMPLETE:
		saveMenuComplete();
		break;
	case SAVEMENU_FAIL:
		saveMenuFail();
		break;
	case SAVEMENU_FORMATYN:
		saveMenuFormatYN();
		break;
	case SAVEMENU_FORMAT:
		saveMenuFormat();
		break;
	case SAVEMENU_FORMATFAIL:
		saveMenuFormatFail();
		break;
	}
}


// ================================================================

//#define HUTSAVE_OK_DELAY		20
//#define HUTSAVE_IGNORE_DELAY	30
//#define HUTSAVE_WARNING_DELAY	150

// Cycle'n'Draw for fowlers-hut gamesave
void cardGameSaveDraw()
{
	optionsDrawOnly();

	optionsAlphaRectangle(-200, -70, 400, loadSaveNumLines*12 + 10, 0, 0x202020);//0x001020);
	optionsAlphaRectangle(-200, card_menu_y - 5, 400, (2*20)+5, 0, 0x202020);//0x001020);

// Switched to Action Man 2 stuff
//	fontPrintCentred(font,0,CARD_HEADING_Y, TEXTSTR(TEXTSTR_SAVE_SAVE),128,128,128);
//	cardPrintMessage();
}

void cardSplashSaveDraw()
{
	levflowSplashDrawOnly();

// Switched to Action Man 2 stuff
//	fontPrintCentred(font,0,CARD_HEADING_Y, TEXTSTR(TEXTSTR_SAVE_SAVE),128,128,128);
//	cardPrintMessage();
}


void cardGameSaveExit()
{
	if(cardState.from == CARD_FROM_FOWLERS)
	{
		DB("Options Return\n");
		optionsReturn();
	}
	else
	{
		DB("Splash Return\n");
		levflowSplashSaveReturn();
	}
	ShowLCDLogo();
}

void cardGameSaveCycle()
{
	// Switch across to the Action Man 2 stuff...
	if (street.saveFrame>0)
	{
		streetChooseSave();
	}
	else
	{
		cardGameSaveExit();
	}
}

void cardFowlerSaveStart()
{
	int rval;

	utilPrintf("SAVE START\n");

	streetStartChooseSave();

	draw_routine = selectVMUDraw;
	cycle_routine = selectVMUCycle;

	ClearCardsPresent();

	gotoLoad = NO;
	
	cardState.from = CARD_FROM_FOWLERS;
	card_message_y = -65;
	card_menu_y = -65 + 62 + 60;

	cardState.doing = CARD_HUTSAVE_INIT;
	cardState.ticker = 0;
	cardState.message = 0;
}

void cardSplashSaveStart()
{
	int rval;

	utilPrintf("SAVE START\n");
	streetStartChooseSave();

	draw_routine = selectVMUDraw;
	cycle_routine = selectVMUCycle;

	ClearCardsPresent();

	gotoLoad = 2;

	cardState.from = CARD_FROM_SPLASH;
	card_message_y = -65 + 10;
	card_menu_y = -65 + 52 + 20 + 60;
	cardState.doing = CARD_HUTSAVE_INIT;
	cardState.ticker = 0;
	cardState.message = 0;
}




int noFreeSpace = YES,noVMUs = YES;
void selectVMUCycle()
{
	int pad,j,lastStatus,lastVM;

	if(gotoLoad == 1)
		noFreeSpace = NO;
	else
		noFreeSpace = YES;
	noVMUs = YES;
	drawLCD = NO;

	for(pad = 0;pad < 4;pad++)
	{
		if((per[pad] == 0) || (per[pad]->id == 0))
			continue;

		// each with 6 possible expansions.
		for(j = 0;j < 2;j++)
		{
			// Port calculated from pad*6 (6 ports per pad) plus j (2 ports available as backup devices)
			// plus 1 because PDD_PORT_x0 is the actual controller
			portIndex = (pad*2) + j;
			vmuPortToUse = portNos[portIndex];
			// 2 drives per controller
			vmuDriveToUse = portIndex;

			// For some reason buGetLastError() prefers FILENOTFOUND over DISKFULL (Duuuuh)
			// so check for full disk separately first.
//			do{ res = buGetDiskFree(vmuDriveToUse,BUD_FILETYPE_NORMAL); } 
//			while( res == BUD_ERR_BUSY );

//			if( res >= 0 && res < VMU_NUM_BLOCKS)
//				res = CARDREAD_NOTFOUNDANDFULL;
//			else


			lastStatus = vmuStatus[pad][j];
			vmuStatus[pad][j] = cardPresent();
			if(vmuStatus[pad][j] == CARDREAD_OK)
			{
				if(lastStatus != CARDREAD_OK)
				{
					vmuStatus[pad][j] = CheckVMUs(&vmuSpace[pad][j]);
					if(vmuStatus[pad][j] == CARDREAD_OK)
						vmuGame[pad][j] = 1;
					else
						vmuGame[pad][j] = 0;
					if(vmuStatus[pad][j] != CARDREAD_NOTFOUNDANDFULL)
						vmuStatus[pad][j] = CARDREAD_OK;
				}
				noVMUs = NO;
			}
			if(vmuStatus[pad][j] == CARDREAD_OK)
				noFreeSpace = NO;
		}
	}


	if((per[selectedPad] == 0) || (per[selectedPad]->id == 0))
		pReadPad->debounce[0] |= PAD_RIGHT;

	if(vmuStatus[selectedPad][selectedVMU] == CARDREAD_NOCARD)
	{
		if(vmuStatus[selectedPad][1 - selectedVMU] == CARDREAD_OK)
			selectedVMU = 1 - selectedVMU;
		else
			pReadPad->debounce[0] |= PAD_RIGHT;
	}


	if((!noVMUs) && (!noFreeSpace))
	{
		lastVM = selectedPad*2 + selectedVMU;
		if(pReadPad->debounce[0] & PAD_LEFT)
		{
			for(pad = (selectedPad + 3) % 4;pad != selectedPad;pad = (pad + 3) % 4)
			{
				if((per[pad]) && (per[pad]->id))
				{
					if(vmuStatus[pad][selectedVMU] != CARDREAD_NOCARD)
					{
						selectedPad = pad;
						break;
					}
					else if(vmuStatus[pad][1 - selectedVMU] != CARDREAD_NOCARD)
					{
						selectedPad = pad;
						selectedVMU = 1 - selectedVMU;
						break;
					}
				}
			}
		}
		if(pReadPad->debounce[0] & PAD_RIGHT)
		{
			for(pad = (selectedPad + 1) % 4;pad != selectedPad;pad = (pad + 1) % 4)
			{
				if((per[pad]) && (per[pad]->id))
				{
					if(vmuStatus[pad][selectedVMU] != CARDREAD_NOCARD)
					{
						selectedPad = pad;
						break;
					}
					else if(vmuStatus[pad][1 - selectedVMU] != CARDREAD_NOCARD)
					{
						selectedPad = pad;
						selectedVMU = 1 - selectedVMU;
						break;
					}
				}
			}
		}
		if((pReadPad->debounce[0] & PAD_UP) && (selectedVMU == 1))
		{
			if(vmuStatus[selectedPad][0] != CARDREAD_NOCARD)
			{
				selectedVMU = 0;
			}
		}
		if((pReadPad->debounce[0] & PAD_DOWN) && (selectedVMU == 0))
		{
			if(vmuStatus[selectedPad][1] != CARDREAD_NOCARD)
			{
				selectedVMU = 1;
			}
		}

		if(selectedPad*2 + selectedVMU != lastVM)
			soundPlaySample(sfxFindSampleInAllBanks("clik1"),0xffff,1000);

		if(pReadPad->debounce[0] & PAD_CROSS)
		{
			soundPlaySample(sfxFindSampleInAllBanks("bleepselect"),0xffff,1000);
			portIndex = selectedPad*2 + selectedVMU;
			vmuPortToUse = portNos[portIndex];
			vmuDriveToUse = portIndex;

			if(gotoLoad == 1)
			{
				draw_routine = cardGameLoadDraw;
				cycle_routine = cardGameLoadCycle;
			}
			else if(gotoLoad == 0)
			{
				draw_routine = cardGameSaveDraw;
				cycle_routine = cardGameSaveCycle;
				streetStartChooseSave();
			}
			else
			{
				draw_routine = cardSplashSaveDraw;
				cycle_routine = cardGameSaveCycle;
				streetStartChooseSave();
			}
		}
	}

	if(pReadPad->debounce[0] & PAD_CIRCLE)
	{
		cardGameLoadExit();
		soundPlaySample(sfxFindSampleInAllBanks("menucloses1"),0xffff,1000);
	}
	drawLCD = YES;
}

int vmutexty = -35;
int vmutexty2 = 60;
int vmutexty3 = 0;
int vmtextx = -216;
int vmtexty = -112;
int vmusprx = 32;
int vmuspry = 25;
void selectVMUDraw()
{
	int pad,j,size,numLines;
	unsigned int selectedCol;
	char *str,str2[16];

	optionsPhaseMenuRGB();
	selectedCol = (optionTextRGB<<16) + (optionTextRGB<<8) + (optionTextRGB) + 0x303030;

	if(vmuSprite == NULL)
	{
		vmuSprite = textureFindInAllBanks("DC_VMU");
	}
	if(vmuSprite2 == NULL)
	{
		vmuSprite2 = textureFindInAllBanks("GINGICON");
	}
	if(padSprite == NULL)
	{
		padSprite = textureFindInAllBanks("DC_CONTROL");
	}


	if(gotoLoad != 2)
	{
		GsSetProjection(800);
		mycamera=currentcamera;
		mapCamSetRefView2L(&mycamera);

		mapviewBasicMapDraw();
		scenicCycle();
	}
	else
	{
		dcTim_Draw(0,0,640,480,splashImg);
	}

	if(pauseOn)
		return;

//	fontPrintCentred(font,0,105,TEXTSTR(TEXTSTR_OPTIONS_SELECT_CLOSE),128,128,128);

	for(pad = 0;pad < 4;pad++)
	{
		if((per[pad] == 0) || (per[pad]->id == 0))
			continue;


		//draw pad graphic

		if((pad == selectedPad) && (!noVMUs) && (!noFreeSpace))
		{
//			fontPrint(font,pad*100 + 50 - 256,-100,"PAD",128,128,128);
			overlaySpriteNoScale(padSprite, pad*140 + 40, 70, 1, selectedCol);
		}
		else
		{
//			fontPrint(font,pad*100 + 50 - 256,-100,"PAD",32,32,32);
			overlaySpriteNoScale(padSprite, pad*140 + 40, 70, 1, 0x404040);
		}


		fontPrintCentred(font,((pad*140 + 40 + 64)*512)/640 - 256 + 2,vmutexty,portName[pad],128,128,128);
//		fontPrintCentred(font,((pad*140 + 40 + 64)*512)/640 - 256 + 2,vmutexty,portName[pad],128,128,128);

		for(j = 0;j < 2;j++)
		{
			sprintf(str2,"%d",j+1);
			fontPrint(font,30 - 256,j*vmutexty2 + vmutexty3,str2,128,128,128);
			switch(vmuStatus[pad][j])
			{
				case CARDREAD_NOCARD:
				case -1:
					break;

				default:
					//draw card graphic
					sprintf(str2,"%d",vmuSpace[pad][j]);
					if((pad == selectedPad) && (j == selectedVMU) && (!noVMUs) && (!noFreeSpace))
					{
						overlaySpriteNoScale(vmuSprite, pad*140 + 56, 200 + j*110, 1, selectedCol);
						if(vmuGame[pad][j])
							overlaySpriteNoScale(vmuSprite2, pad*140 + 56 + vmusprx, 200 + j*110 + vmuspry, 1, selectedCol);
						else
							fontPrintCentred(font,((pad*140 + 56)*512)/640 + vmtextx,((200 + j*110)*256)/480 + vmtexty,str2,optionTextRGB,optionTextRGB,optionTextRGB);
					}
//						fontPrint(font,pad*100 + 50 - 256,-100 + (j + 1)*80,"CARD",128,128,128);
					else
					{
						overlaySpriteNoScale(vmuSprite, pad*140 + 56, 200 + j*110, 1, 0x404040);
						if(vmuGame[pad][j])
							overlaySpriteNoScale(vmuSprite2, pad*140 + 56 + vmusprx, 200 + j*110 + vmuspry, 1, 0x404040);
						else
							fontPrintCentred(font,((pad*140 + 56)*512)/640 + vmtextx,((200 + j*110)*256)/480 + vmtexty,str2,64,64,64);
					}
//						fontPrint(font,pad*100 + 50 - 256,-100 + (j + 1)*80,"CARD",32,32,32);
				
					break;
			}
		}
	}
	if(noVMUs)
	{
		if(!pauseOn)
		{
			if(gotoLoad == 1)
				numLines = fontCentredMultiLine(font,0,0,512-100,TEXTSTR(TEXTSTR_MCARD_NO_VM_AT_ALL),128,128,128);
			else
				numLines = fontCentredMultiLine(font,0,0,512-100,TEXTSTR(TEXTSTR_MCARD_NO_SAVE_POSSIBLE),128,128,128);

			optionsAlphaRectangle(-216,-5,512-80,numLines*12 + 10,0,0x808080);
		}

		str = TEXTSTR(TEXTSTR_OPTIONS_CLOSE_MENU);
	}
	else if(noFreeSpace)
	{
		if(!pauseOn)
		{
//			fontCentredMultiLine(font,0,-110,512-40,TEXTSTR(TEXTSTR_MCARD_NO_VM_WITH_SPACE),128,128,128);

			numLines = fontCentredMultiLine(font,0,0,512-100,TEXTSTR(TEXTSTR_MCARD_NO_SAVE_POSSIBLE),128,128,128);

			optionsAlphaRectangle(-216,-5,512-80,numLines*12 + 10,0,0x808080);
		}

		str = TEXTSTR(TEXTSTR_OPTIONS_CLOSE_MENU);
	}
	else
	{
		if(!pauseOn)
		{
			str = TEXTSTR(TEXTSTR_OPTIONS_SELECT_CLOSE);
			if(gotoLoad == 1)
				fontCentredMultiLine(font,0,-110,512-40,TEXTSTR(TEXTSTR_MCARD_SELECT_VM_FOR_LOAD),128,128,128);
			else
				fontCentredMultiLine(font,0,-110,512-40,TEXTSTR(TEXTSTR_MCARD_SELECT_VM_FOR_SAVE),128,128,128);
		}
	}

	if(!pauseOn)
	{
		size = fontExtentW(font,str);
		fontPrint(font,-(size/2), BOTTOM_WINDOW_Y+ 42 - 400/8,str,128,128,128);
		optionsAlphaRectangle( (-(size)/2)-4,BOTTOM_WINDOW_Y+ 37 - 400/8, (size)+8,18, 0, 0x003000);
	}
}

extern int lcdTimeout;
int cardPresent()
															 

{
	int timeOut = 0;

	while(!pdVmsLcdIsReady(vmuPortToUse))
	{
		if(timeOut++ > lcdTimeout)
			return 	CARDREAD_NOCARD;
	}
	return CARDREAD_OK;
}

void ShowLCDLogoOnAllCards()
{
	int vmuPortStore = vmuPortToUse;
	int vmuDriveStore = vmuDriveToUse;
	int i,j;

	lcdTimeout *= 100;

	for(i = 0;i < 4;i++)
	{
		if((per[i]) && (per[i]->info) && (per[i]->info->type))
		{
			for(j = 0;j < 2;j++)
			{
				vmuDriveToUse = i*2 + j;
				vmuPortToUse = portNos[vmuDriveToUse];
				ShowLCDLogo();
			}
		}
	}
	vmuPortToUse = vmuPortStore;
	vmuDriveToUse = vmuDriveStore;

	lcdTimeout /= 100;
}
