// Dude's handy memory card stuff for Tomb Raider!!!
//
// Started 19 Aug 96

/*---------------------------------------------------------------------------
 *	Import Headers
\*--------------------------------------------------------------------------*/

#include <sys\types.h>
#include <sys\file.h>
#include <libapi.h>
#include <stdio.h>
#include "typedefs.h"
#include "gpu.h"
#include "picture.h"
#include "memcard.h"
#include "psxinput.h"
#include "specific.h"
#include "memory.h"
#include "..\game\camera.h"
#include "..\game\control.h"
#include "..\game\input.h"
#include "..\game\text.h"
#include "..\game\gameflow.h"
#include "..\game\inventry.h"
#include "..\game\invfunc.h"

/*---------------------------------------------------------------------------
 *	Constants
\*--------------------------------------------------------------------------*/

#define USE_FADES		// Un-comment this to re-implement fades
//#define LOG

#define EVENT_TIMEOUT 200

#ifdef NTSC
#define SAVEGAME_FILENAME GF_PSXStrings[PSSTR_FILENAMENTSC]
#else
#define SAVEGAME_FILENAME GF_PSXStrings[PSSTR_FILENAMEPAL]
#endif

#define SAVEGAME_SLOTNAME "bu00:"
#define SAVEGAME_SLOTWILD "bu00:*"

enum	{
	SW_EVENTS,
	HW_EVENTS
};

enum {
	NO_EVENT= -1,
	EV_IOE,
	EV_ERROR,
	EV_TIMEOUT,
	EV_NEW
};

enum {
	CARD_OK,
	CARD_ERROR,
	CARD_NONE,
	CARD_NEW_FORMATTED,
	CARD_NEW_UNFORMATTED,
	CARD_NEW
};

enum	{
	MCS_UNKNOWN,
	MCS_FILE_EXISTS,
	MCS_CAN_CREATE,
	MCS_CARD_FULL,
	MCS_ERROR,
	MCS_NO_CARD,
	MCS_UNFORMATTED
};

enum {
	MENU_INIT,
	MENU_NONE,
	MENU_OVERWRITE,
	MENU_OVERWRITE_CONFIRM,
	MENU_SAVE,
	MENU_CARD_FULL,
	MENU_LOAD,
	MENU_NO_GAME,
	MENU_ERROR,
	MENU_NO_CARD,
	MENU_FORMAT,
	MENU_FORMAT_CONFIRM,
	MENU_SAVING,
	MENU_LOADING,
	MENU_FORMATTING,
	MENU_NO_FRONTEND,
	MENU_FRONTEND_EXISTS,
	MENU_FRONTEND_CAN_CREATE,
	MENU_FRONTEND_NEEDS_FORMAT,
	MENU_NOT_FORMATTED,
	MENU_SAVE_CONFIRM,
	I_HATE_DUDE
	};

enum	{
	MCO_SAVE_GAME,
	MCO_LOAD_GAME,
	MCO_FRONTEND_CHECK
};

enum	{
	TEXT_E_SAVE_GAME_TO,
	TEXT_E_OVERWRITE_GAME_ON,
	TEXT_E_LOAD_GAME_FROM,
	TEXT_E_THE_MEMORY_CARD_IN,
	TEXT_E_MEMORY_CARD_SLOT_1,
	TEXT_E_ARE_YOU_SURE,
	TEXT_E_YES,
	TEXT_E_NO,
	TEXT_E_IS_FULL,
	TEXT_E_THERE_ARE_NO,
	TEXT_E_TOMB_RAIDER_II_GAMES_ON,
	TEXT_E_THERE_IS_A_PROBLEM_WITH,
	TEXT_E_THERE_IS_NO_MEMORY_CARD_IN,
	TEXT_E_IS_UNFORMATTED,
	TEXT_E_WOULD_YOU_LIKE_TO,
	TEXT_E_FORMAT_IT,
	TEXT_E_SAVING_GAME_TO,
	TEXT_E_LOADING_GAME_FROM,
	TEXT_E_FORMATTING,
	TEXT_E_OVERWRITE,
	TEXT_E_SAVE,
	TEXT_E_LOAD,
	TEXT_E_FORMAT_MEMORY_CARD,
	TEXT_E_EXIT,
	TEXT_E_CONTINUE,
	TEXT_E_YOU_WILL_NOT_BE_ABLE,
	TEXT_E_TO_SAVE_ANY_GAMES_UNLESS,
	TEXT_E_YOU_INSERT_A_FORMATTED,
	TEXT_E_MEMORY_CARD_WITH_AT_LEAST,
	TEXT_E_ONE_FREE_BLOCK,
	TEXT_E_capital_THE_MEMORY_CARD_IN,
	TEXT_E_EXIT_WITHOUT_SAVING,
	TEXT_E_EXIT_WITHOUT_LOADING,
	TEXT_E_START_GAME,

	TEXT_E_UNFORMATTED_MEMORY_CARD,
	TEXT_E_INSERT_A_FORMATTED,
	TEXT_E_OR_PRESS_X_TO_CONTINUE,
	TEXT_E_WITHOUT_SAVING
};

/*---------------------------------------------------------------------------
 *	Type Definitions
\*--------------------------------------------------------------------------*/

typedef struct {
	uint16	wMagic;
	uint8	bType;
	uint8	bBlockEntry;
	uint16	awTitle[32];
	uint8	bUnused[28];
	uint16	awClut[16];
	uint8	aabIcon[3][128];
} FileHeader;

/*---------------------------------------------------------------------------
 *	Macros
\*--------------------------------------------------------------------------*/

#ifdef LOG
#define Log_(args...) DebugOut(args)
#else
#define Log_(args...)
#endif

#define align(thing,size) (((thing)+(size)-1)&(~((size)-1)))

#define GetString(enum) GF_PSXStrings[PSSTR_GAPSTRING0+enum]

/*---------------------------------------------------------------------------
 *	Externals
\*--------------------------------------------------------------------------*/

extern void DrawMonoScreen(char r, char g, char b);
sint16	JustSaved,NewAdventureLoadGame;

/*---------------------------------------------------------------------------
 *	Locals
\*--------------------------------------------------------------------------*/

#ifdef MEMCARD

static int padremoved=0;
static TEXTSTRING	*padtxt=NULL;
static TEXTSTRING   *prompttxt=NULL;

static int LnCurStatus=MCS_UNKNOWN;				// card 1 status, initially unknown
static uint32 LaadwEvents[2][4];

static int LnExitMessage;

/*---------------------------------------------------------------------------
 *	Local Functions
\*--------------------------------------------------------------------------*/

static void OpenEvents(void)
{
	static uint32 adwType[2]={SwCARD,HwCARD};
	static uint32 adwEvent[4]={EvSpIOE,EvSpERROR,EvSpTIMOUT,EvSpNEW};
	int nType,nEvent;

	EnterCriticalSection();

	for ( nType=SW_EVENTS ; nType<=HW_EVENTS ; nType++ )
	{
		for ( nEvent=EV_IOE ; nEvent<=EV_NEW ; nEvent++ )
	 	{
			LaadwEvents[nType][nEvent]=OpenEvent(adwType[nType],adwEvent[nEvent],EvMdNOINTR,NULL);
			EnableEvent(LaadwEvents[nType][nEvent]);
	 	}
	}
	ExitCriticalSection();
}

static void CloseEvents(void)
{
	int nType,nEvent;

	EnterCriticalSection();

	for ( nType=SW_EVENTS ; nType<=HW_EVENTS ; nType++ )
	{
		for ( nEvent=EV_IOE ; nEvent<=EV_NEW ; nEvent++ )
		{
			DisableEvent(LaadwEvents[nType][nEvent]);
			CloseEvent(LaadwEvents[nType][nEvent]);
		}
	}
	ExitCriticalSection();
}

static void ClearEvents(int nType)
{
	int nEvent;

	for ( nEvent=EV_IOE ; nEvent<=EV_NEW ; nEvent++ )
		TestEvent(LaadwEvents[nType][nEvent]);
}

static int GetEvent(int nType)
{
	uint32 dwFrames;
	int nEvent;

	dwFrames=GnFrameCounter+EVENT_TIMEOUT;

	while (GnFrameCounter<dwFrames)
	{
		for ( nEvent=EV_IOE ; nEvent<=EV_NEW ; nEvent++)
		{
			if (TestEvent(LaadwEvents[nType][nEvent]))
				return nEvent;
		}
	}
	return NO_EVENT;
}

static int CardInfo(void)
{
	Log_("CardInfo: ");

	ClearEvents(SW_EVENTS);

	while (_card_info(0)==0)
		;

	switch (GetEvent(SW_EVENTS))
	{
		case NO_EVENT:
			Log_("timeout");
			return CARD_ERROR;

		case EV_IOE:
			Log_("card ok, no change");
			return CARD_OK;

		default:
		case EV_ERROR:
			Log_("bad card");
			return CARD_ERROR;

		case EV_TIMEOUT:
			Log_("no card");
			return CARD_NONE;

		case EV_NEW:
			Log_("new card");
			return CARD_NEW;
	}
}

static int CardClear(void)
{
	Log_(": CardClear: ");

	ClearEvents(HW_EVENTS);

	while (_card_clear(0)==0)
		;

	switch (GetEvent(HW_EVENTS))
	{
		case NO_EVENT:
			Log_("timeout");
			return CARD_ERROR;

		case EV_TIMEOUT:
			Log_("no card");
			return CARD_NONE;

		case EV_IOE:
			Log_("ok");
			return CARD_OK;

		case EV_ERROR:
		default:
			Log_("bad card");
			return CARD_ERROR;

	}
}

static int CardLoad(void)
{
	Log_(": CardLoad: ");

	ClearEvents(SW_EVENTS);

	while (_card_load(0)==0)
		;

	switch (GetEvent(SW_EVENTS))
	{
		case NO_EVENT:
			Log_("timeout");
			return CARD_ERROR;

		case EV_IOE:
			Log_("ok, formatted");
			return CARD_NEW_FORMATTED;

		case EV_TIMEOUT:
			Log_("no card");
			return CARD_NONE;

		case EV_NEW:
			Log_("ok, unformatted");
			return CARD_NEW_UNFORMATTED;

		case EV_ERROR:
		default:
			Log_("bad card");
			return CARD_ERROR;

	}
}

static int CheckSlot(bool tStateUnknown)
{
	int nRetVal;

	nRetVal=CardInfo();

	if (nRetVal == CARD_NEW)
	{
		nRetVal = CardClear();

		if (nRetVal == CARD_OK)
			nRetVal = CardLoad();
	}
	else if ((nRetVal==CARD_OK)&&tStateUnknown)
		nRetVal=CardLoad();

	Log_("\n");

	return nRetVal;
}

static int CheckMyFileStatus(void)
{
	int fh;
	FileHeader FH;
	int nBlocksFree;
	struct DIRENTRY d;

	fh = open(SAVEGAME_FILENAME, O_RDONLY);
	Log_("Read '%s'\n", SAVEGAME_FILENAME);

	if (fh!= -1)
	{
		if ((read(fh, &FH, sizeof(FileHeader))== -1)
		||  (close(fh) == -1)
		||  (FH.wMagic != 0x4353)
		||  (FH.bType != 0x11)
		||  (FH.bBlockEntry != 2))
			return MCS_CAN_CREATE;		// hmm...
		else
			return MCS_FILE_EXISTS;
	}

	nBlocksFree=15;

	if ((struct DIRENTRY *)firstfile(SAVEGAME_SLOTWILD,&d)==&d)
	{
		do {
			nBlocksFree-=((d.size+8191)>>13);
		} while ((struct DIRENTRY *)nextfile(&d)==&d);
	}

	return (nBlocksFree >= 2) ? MCS_CAN_CREATE : MCS_CARD_FULL;
}

static int SaveData(uint8 *pData,int nBytes)
{
	int fh;

	Log_(__FUNCTION__ "pData=$%x; nBytes=$%x\n",(unsigned)pData,nBytes);
	LnCurStatus=MCS_UNKNOWN;

	fh = open(SAVEGAME_FILENAME, O_WRONLY);
	Log_("Write '%s'\n", SAVEGAME_FILENAME);

	if (fh == -1)
	{
		fh = open(SAVEGAME_FILENAME, O_CREAT|0x20000);
		Log_("Create '%s'\n", SAVEGAME_FILENAME);
	}

	if ((fh == -1)
	||  (write(fh, GabMemoryCardHeader, sizeof(FileHeader)) == -1)
	||  (write(fh, pData, nBytes) == -1)
	||  (close(fh) == -1))
		return 0;
	else
		return 1;
}

static int LoadData(uint8 *pData,int nBytes)
{
	int fh;
	FileHeader FH;		// :)-~

	fh = open(SAVEGAME_FILENAME, O_RDONLY);
	Log_("Write '%s'\n", SAVEGAME_FILENAME);

	if ((fh == -1)
	||  (read(fh, &FH, sizeof(FileHeader)) == -1)
	||  (read(fh, pData, nBytes) == -1)
	||  (close(fh) == -1)
	||  (FH.wMagic != 0x4353)
	||  (FH.bType != 0x11)
	||  (FH.bBlockEntry != 2))
		return 0;
	else
		return 1;
}

static int FormatCard()
{
	LnCurStatus = MCS_UNKNOWN;
	return format(SAVEGAME_SLOTNAME);
}

static int GetCardStatus(void)
{
	switch (CheckSlot(LnCurStatus == MCS_UNKNOWN))	// don't want CARD_OK first time
	{
		case CARD_NEW_FORMATTED:
			LnCurStatus = CheckMyFileStatus();		// if new & formatted then look for saved game file
			break;

		case CARD_OK:
			break;							// no change in status

		case CARD_ERROR:
			LnCurStatus=MCS_ERROR;
			break;

		case CARD_NONE:
			LnCurStatus=MCS_NO_CARD;
			break;

		case CARD_NEW_UNFORMATTED:
			LnCurStatus=MCS_UNFORMATTED;
			break;
	}

	return LnCurStatus;		// cannot be MCS_UNKNOWN!
}

static void DrawTheScreen(void)
{
	S_InitialisePolyList();

	if (fades_enabled)
		S_DrawPic();
	else
//		DoInventoryBackground();
		DrawMonoScreen(0x50, 0x50, 0x70);
	T_DrawText();
	S_OutputPolyList();
	S_DumpScreen();
}

#define NMSTR 10
#define MC_TEXT_START	32+35
#define MC_MENU_START	128+40


static int DoMenu(int nMenu)
{
	static TEXTSTRING *apTS[NMSTR],*apMITS[2];
	static int 		nCurMenu,nCurMenuItem,nMenuItems;
	static uint32 	dwOldInput;
	int 			nString,nConfirmString,nStash;
//	int				width,maxw;
	int				nY  = MC_TEXT_START;
	int				nYs = MC_MENU_START;

	bool tWhackUpExit,tInfoOnly,tWhackUpContinue;

#ifdef JAPAN
#define WHACK_UPX(s,x) {apTS[nString++]=T_Print(x,nY,0,GetString(s));nY+=16;}
#endif
#define WHACK_UP(s) {apTS[nString]=T_Print(0,nY,0,GetString(s));T_CentreH(apTS[nString++],TRUE);nY+=16;}
#define WHACK_UP_WITH_STASH(s) {apMITS[nStash++]=apTS[nString]=T_Print(0,nYs,0,GetString(s));T_CentreH(apTS[nString++],TRUE);nYs+=16;}


	nString=0;

	if (nMenu==0)
	{
		while (nString<NMSTR)
			apTS[nString++] = 0;

		nCurMenu = 0;
		return 0;
	}

	if (nMenu != nCurMenu)
	{
		nCurMenu = nMenu;

		while (nString < NMSTR)
		{
			if (apTS[nString])
				T_RemovePrint(apTS[nString]);
			apTS[nString++]=0;
		}

		nString=0;
		nConfirmString= -1;
		tWhackUpExit=TRUE;
		tInfoOnly=FALSE;
		tWhackUpContinue=FALSE;
		nCurMenuItem=0;

		switch (nMenu)
		{
			case MENU_OVERWRITE:
				nY+=24;
#ifdef JAPAN
				WHACK_UPX(TEXT_E_OVERWRITE_GAME_ON,96)
//				WHACK_UPX(TEXT_E_THE_MEMORY_CARD_IN,96)
				WHACK_UPX(TEXT_E_MEMORY_CARD_SLOT_1,96)
#else
				WHACK_UP(TEXT_E_OVERWRITE_GAME_ON)
				WHACK_UP(TEXT_E_THE_MEMORY_CARD_IN)
				WHACK_UP(TEXT_E_MEMORY_CARD_SLOT_1)
#endif
				nConfirmString=TEXT_E_OVERWRITE;
//				nCurMenuItem=1;	// default: NO (for stupid lamers)
				break;

			case MENU_SAVE:
				nY+=24;
#ifdef JAPAN
				WHACK_UPX(TEXT_E_SAVE_GAME_TO,104)
//				WHACK_UPX(TEXT_E_THE_MEMORY_CARD_IN,104)
				WHACK_UPX(TEXT_E_MEMORY_CARD_SLOT_1,104)
#else
				WHACK_UP(TEXT_E_SAVE_GAME_TO)
				WHACK_UP(TEXT_E_THE_MEMORY_CARD_IN)
				WHACK_UP(TEXT_E_MEMORY_CARD_SLOT_1)
#endif
				nConfirmString=TEXT_E_SAVE;
				break;

			case MENU_LOAD:
				nY+=24;
#ifdef JAPAN
				WHACK_UPX(TEXT_E_LOAD_GAME_FROM,104)
//				WHACK_UPX(TEXT_E_THE_MEMORY_CARD_IN,104)
				WHACK_UPX(TEXT_E_MEMORY_CARD_SLOT_1,104)
#else
				WHACK_UP(TEXT_E_LOAD_GAME_FROM)
				WHACK_UP(TEXT_E_THE_MEMORY_CARD_IN)
				WHACK_UP(TEXT_E_MEMORY_CARD_SLOT_1)
#endif
				nConfirmString=TEXT_E_LOAD;
				break;

			case MENU_SAVE_CONFIRM:		// duh...
			case MENU_FORMAT_CONFIRM:
			case MENU_OVERWRITE_CONFIRM:
				nY+=40;
				WHACK_UP(TEXT_E_ARE_YOU_SURE)
				nConfirmString=TEXT_E_YES;
				nCurMenuItem=1;	// default: NO (for stupid lamers)
				tWhackUpExit=FALSE;
				break;

			case MENU_CARD_FULL:
				nY+=24;
#ifdef JAPAN
//				WHACK_UPX(TEXT_E_capital_THE_MEMORY_CARD_IN,104)
				WHACK_UPX(TEXT_E_MEMORY_CARD_SLOT_1,104)
				WHACK_UPX(TEXT_E_IS_FULL,104)
#else
				WHACK_UP(TEXT_E_capital_THE_MEMORY_CARD_IN)
				WHACK_UP(TEXT_E_MEMORY_CARD_SLOT_1)
				WHACK_UP(TEXT_E_IS_FULL)
#endif
				break;

			case MENU_NO_GAME:
				nY+=16;
#ifdef JAPAN
				WHACK_UPX(TEXT_E_MEMORY_CARD_SLOT_1,96)
				WHACK_UPX(TEXT_E_THERE_ARE_NO,96)
				WHACK_UPX(TEXT_E_TOMB_RAIDER_II_GAMES_ON,96)
//				WHACK_UPX(TEXT_E_THE_MEMORY_CARD_IN,96)
#else
				WHACK_UP(TEXT_E_THERE_ARE_NO)
				WHACK_UP(TEXT_E_TOMB_RAIDER_II_GAMES_ON)
				WHACK_UP(TEXT_E_THE_MEMORY_CARD_IN)
				WHACK_UP(TEXT_E_MEMORY_CARD_SLOT_1)
#endif
				break;

			case MENU_ERROR:
				nY+=24;
#ifdef JAPAN
				WHACK_UPX(TEXT_E_MEMORY_CARD_SLOT_1,46)
				WHACK_UPX(TEXT_E_THERE_IS_A_PROBLEM_WITH,46)
//				WHACK_UPX(TEXT_E_THE_MEMORY_CARD_IN,92)
#else
				WHACK_UP(TEXT_E_THERE_IS_A_PROBLEM_WITH)
				WHACK_UP(TEXT_E_THE_MEMORY_CARD_IN)
				WHACK_UP(TEXT_E_MEMORY_CARD_SLOT_1)
#endif
				break;

			case MENU_NO_CARD:
				nY+=32;
#ifdef JAPAN
				WHACK_UPX(TEXT_E_MEMORY_CARD_SLOT_1,80)
				WHACK_UPX(TEXT_E_THERE_IS_NO_MEMORY_CARD_IN,80)
#else
				WHACK_UP(TEXT_E_THERE_IS_NO_MEMORY_CARD_IN)
				WHACK_UP(TEXT_E_MEMORY_CARD_SLOT_1)
#endif
				tWhackUpContinue=TRUE;
//##				tInfoOnly=TRUE;

				break;

			case MENU_NOT_FORMATTED:
				nY+=24;
#ifdef JAPAN
//				WHACK_UPX(TEXT_E_capital_THE_MEMORY_CARD_IN,96)
				WHACK_UPX(TEXT_E_MEMORY_CARD_SLOT_1,96)
				WHACK_UPX(TEXT_E_IS_UNFORMATTED,96)
#else
				WHACK_UP(TEXT_E_capital_THE_MEMORY_CARD_IN)
				WHACK_UP(TEXT_E_MEMORY_CARD_SLOT_1)
				WHACK_UP(TEXT_E_IS_UNFORMATTED)
#endif
				break;

			case MENU_FORMAT:
			case MENU_FRONTEND_NEEDS_FORMAT:
#ifdef JAPAN
//				WHACK_UPX(TEXT_E_capital_THE_MEMORY_CARD_IN,96)
				WHACK_UPX(TEXT_E_MEMORY_CARD_SLOT_1,72)
				WHACK_UPX(TEXT_E_IS_UNFORMATTED,72)
#else
				WHACK_UP(TEXT_E_capital_THE_MEMORY_CARD_IN)
				WHACK_UP(TEXT_E_MEMORY_CARD_SLOT_1)
				WHACK_UP(TEXT_E_IS_UNFORMATTED)
#endif
				nY+=8;
#ifdef JAPAN
				WHACK_UPX(TEXT_E_WOULD_YOU_LIKE_TO,72)
				WHACK_UPX(TEXT_E_FORMAT_IT,72)
#else
				WHACK_UP(TEXT_E_WOULD_YOU_LIKE_TO)
				WHACK_UP(TEXT_E_FORMAT_IT)
#endif
				nConfirmString=TEXT_E_FORMAT_MEMORY_CARD;
//				nCurMenuItem=1;	// default: NO (for stupid American lamers)
				break;
/*
				WHACK_UP(TEXT_E_UNFORMATTED_MEMORY_CARD)
				nY+=8;
				WHACK_UP(TEXT_E_INSERT_A_FORMATTED)
				WHACK_UP(TEXT_E_MEMORY_CARD_WITH_AT_LEAST)
				WHACK_UP(TEXT_E_ONE_FREE_BLOCK)
				WHACK_UP(TEXT_E_OR_PRESS_X_TO_CONTINUE)
				WHACK_UP(TEXT_E_WITHOUT_SAVING)
				nMenuItems = 1;
				break;
*/
			case MENU_SAVING:
			case MENU_LOADING:
			case MENU_FORMATTING:
				nY+=24;
#ifdef JAPAN
				if (nMenu==MENU_SAVING)
					WHACK_UPX(TEXT_E_SAVING_GAME_TO,96)
				else if (nMenu==MENU_LOADING)
					WHACK_UPX(TEXT_E_LOADING_GAME_FROM,96)
				else
					WHACK_UPX(TEXT_E_FORMATTING,96)
//				WHACK_UPX(TEXT_E_THE_MEMORY_CARD_IN,96)
				WHACK_UPX(TEXT_E_MEMORY_CARD_SLOT_1,96)
#else
				if (nMenu==MENU_SAVING)
					WHACK_UP(TEXT_E_SAVING_GAME_TO)
				else if (nMenu==MENU_LOADING)
					WHACK_UP(TEXT_E_LOADING_GAME_FROM)
				else
					WHACK_UP(TEXT_E_FORMATTING)
				WHACK_UP(TEXT_E_THE_MEMORY_CARD_IN)
				WHACK_UP(TEXT_E_MEMORY_CARD_SLOT_1)
#endif
				tInfoOnly=TRUE;
				break;

			case MENU_NO_FRONTEND:
#ifdef JAPAN
				WHACK_UPX(TEXT_E_YOU_WILL_NOT_BE_ABLE,96)
				WHACK_UPX(TEXT_E_TO_SAVE_ANY_GAMES_UNLESS,96)
				WHACK_UPX(TEXT_E_YOU_INSERT_A_FORMATTED,96)
				WHACK_UPX(TEXT_E_MEMORY_CARD_WITH_AT_LEAST,96)
				WHACK_UPX(TEXT_E_ONE_FREE_BLOCK,96)
#else
				WHACK_UP(TEXT_E_YOU_WILL_NOT_BE_ABLE)
				WHACK_UP(TEXT_E_TO_SAVE_ANY_GAMES_UNLESS)
				WHACK_UP(TEXT_E_YOU_INSERT_A_FORMATTED)
				WHACK_UP(TEXT_E_MEMORY_CARD_WITH_AT_LEAST)
				WHACK_UP(TEXT_E_ONE_FREE_BLOCK)
#endif
				tWhackUpContinue=TRUE;
				break;

//			case I_HATE_DUDE:
//				WHACK_UP(TEXT_E_YOU_WILL_NOT_BE_ABLE)
//				break;

			default:
				tInfoOnly=TRUE;
				break;
			}

		if (tInfoOnly)
			nMenuItems=0;
		else
		{
			nStash=0;
			nMenuItems = (nConfirmString>=0) ? 2 : 1;

			if (nMenuItems == 2)
			{
				WHACK_UP_WITH_STASH(nConfirmString)

				if (!tWhackUpExit)
					WHACK_UP_WITH_STASH(TEXT_E_NO)
			}

			if ((nMenuItems == 1)
			||  (tWhackUpExit))
				WHACK_UP_WITH_STASH(tWhackUpContinue ? PSSTR_GAPSTRING0+TEXT_E_CONTINUE : LnExitMessage)	// haha
		}
	}

//	if ( nString )
//	{
//		maxw = 0;
//		for ( nStash=0; nStash<nString; nStash++ )
//		{
//			width = T_GetTextWidth( apTS[nStash] );
//			if ( width>maxw )
//				maxw = width;
//		}
//		if ( maxw && nY-MC_TEXT_START>=16 )
//		{
//			T_AddBackground( apTS[0], maxw+10, nY-MC_TEXT_START-16, 0,0, 48, 0, req_bgnd_gour1, D_TRANS1 );
//			T_AddOutline( apTS[0], 1, 0, req_bgnd_gour2, NULL );
//		}
//	}

	if ( prompttxt )
	{
		T_RemovePrint( prompttxt );
		prompttxt = NULL;
	}

	if (nMenuItems)
	{
		for ( nStash=0 ; nStash<nMenuItems ; nStash++ )
		{
			if (nStash == nCurMenuItem)
			{
				T_AddBackground(apMITS[nStash], 0,0, 0,0,0, C_BLACK, req_sel_gour1, D_TRANS2);
				T_AddOutline(apMITS[nStash],1,C_ORANGE,req_sel_gour2,NULL);
			}
			else
			{
				T_RemoveOutline(apMITS[nStash]);
				T_RemoveBackground(apMITS[nStash]);
			}
		}
		if (nMenuItems == 1)
			prompttxt = T_Print( 0, -30, 2, GF_PSXStrings[PSSTR_CONTINUE_TEXT] );
		else
			prompttxt = T_Print( 0, -30, 2, GF_PSXStrings[PSSTR_SELECT_TEXT] );

		T_BottomAlign( prompttxt, 1 );
		T_CentreH( prompttxt, 1 );
	}

	DrawTheScreen();
	DrawTheScreen();
	S_UpdateInput();

/* -------- process no pad text */

	if ((PadConnected)
	&&  (padremoved)
	&&  (padtxt))
	{
		T_RemovePrint(padtxt); padtxt = NULL;
		padremoved = 0;
	}

	if ((!PadConnected)
	&&  (!padremoved)
	&&  (!padtxt))
	{
  		padtxt = T_Print(0, 50, 9, GF_PSXStrings[PSSTR_NOJOY_TEXT]);
		T_FlashText(padtxt, 1, 20);
   		T_CentreH(padtxt,1);
		padremoved = 1;
	}

	if ((input!=dwOldInput)||(!input))
		dwOldInput=input;
	else
		input=0;

	if (!nMenuItems)
		return 0;
	if (input&IN_SELECT)
		return nCurMenuItem+1;
	if ((input&IN_FORWARD)&&(nCurMenuItem))
		--nCurMenuItem;
	if ((input&IN_BACK)&&(nCurMenuItem<(nMenuItems-1)))
		++nCurMenuItem;
	return 0;
}

/*---------------------------------------------------------------------------
 *	Public
\*--------------------------------------------------------------------------*/

int S_MemoryCardOperation(int nOperation,void *pData,int nBytes)
{
	static int nTransition[3][6] =
		{
/*			MCS_FILE_EXISTS			MCS_CAN_CREATE			MCS_CARD_FULL		MCS_ERROR			MCS_NO_CARD			MCS_UNFORMATTED */
/* save */	{MENU_OVERWRITE,		MENU_SAVE,				MENU_CARD_FULL,		MENU_ERROR,			MENU_NO_CARD,		MENU_FORMAT},
/* load */	{MENU_LOAD,				MENU_NO_GAME,			MENU_NO_GAME,		MENU_ERROR,			MENU_NO_CARD,		MENU_NOT_FORMATTED},

//#if defined(NTSC) && !defined(JAPAN)
//			// US Version of frontend allows the user to format
///* front */	{MENU_FRONTEND_EXISTS,	MENU_FRONTEND_CAN_CREATE,MENU_NO_FRONTEND,	MENU_NO_FRONTEND,	MENU_NO_FRONTEND,	MENU_FRONTEND_NEEDS_FORMAT}
//#else
			// Don't allow formatting in this version
/* front */	{MENU_FRONTEND_EXISTS,	MENU_FRONTEND_CAN_CREATE,MENU_NO_FRONTEND,	MENU_NO_FRONTEND,	MENU_NO_FRONTEND,	MENU_NOT_FORMATTED}
//#endif
		};

	bool tExit;
	int nCardStatus,nNewStatus;
	int nMenu,nSelection,nRetVal;


#ifdef GAMEDEBUG
	printf("S_MemoryCardOperation(%d,%p,%d);\n", nOperation,pData,nBytes);
#endif
	if (nOperation==MCO_SAVE_GAME)
		LnExitMessage=TEXT_E_EXIT_WITHOUT_SAVING;

	else if (nOperation==MCO_LOAD_GAME)
		LnExitMessage=TEXT_E_EXIT_WITHOUT_LOADING;
	else
		LnExitMessage=TEXT_E_START_GAME;

	nBytes=align(nBytes,128);

	T_InitPrint();

	DrawTheScreen();
	DrawTheScreen();
	DrawTheScreen();

	OpenEvents();
	nCardStatus = MCS_UNKNOWN;
	nMenu = MENU_INIT;
	DoMenu(nMenu);

	for ( tExit=FALSE ; !tExit ; )
	{
		nNewStatus = GetCardStatus();

		if (nNewStatus!=nCardStatus)		// maybe the card status has changed???
		{
			nCardStatus=nNewStatus;
			nMenu=nTransition[nOperation][nCardStatus-1];
		}

		if (nMenu == MENU_FRONTEND_EXISTS)
		{
//			DoMenu(I_HATE_DUDE);

			if (LoadData(pData,nBytes))			// early exit, but got some front end settings
				nRetVal=MCF_LOADED;
			else
				nRetVal=MCF_FAILED;
			tExit=TRUE;
			continue;
		}

		if (nMenu==MENU_FRONTEND_CAN_CREATE)
		{
			nRetVal=MCF_CAN_SAVE;			// early exit in frontend mode
			tExit=TRUE;
			continue;
		}

		nSelection=DoMenu(nMenu);			// maybe the user selected something???
		nRetVal=0;							// most retvals are 0

		if (nSelection>0)
			switch (nMenu)
			{
				case MENU_SAVE:
					if (nSelection==1)
						nMenu=MENU_SAVE_CONFIRM;
					else
						tExit=TRUE;
					break;

				case MENU_OVERWRITE:
					if (nSelection==1)
						nMenu=MENU_OVERWRITE_CONFIRM;
					else
						tExit=TRUE;
					break;

				case MENU_SAVE_CONFIRM:
				case MENU_OVERWRITE_CONFIRM:
					if (nSelection==1)
					{
						nMenu=MENU_SAVING;
						DoMenu(nMenu);
						if (SaveData(pData,nBytes))
							nRetVal=MCF_SAVED;			// success
						else
							nRetVal=MCF_FAILED;
					}
					tExit=TRUE;
					break;

				case MENU_LOAD:
					if (nSelection == 1)
					{
						nMenu = MENU_LOADING;

						DoMenu(nMenu);

						if (LoadData(pData, nBytes))
							nRetVal=MCF_LOADED;
						else
							nRetVal=MCF_FAILED;
					}
					tExit=TRUE;
					break;

				case MENU_FORMAT:
					if (nSelection==1)
						nMenu=MENU_FORMAT_CONFIRM;
					else
						tExit=TRUE;
					break;

				case MENU_FORMAT_CONFIRM:
					if (nSelection==1)
						{
						nMenu=MENU_FORMATTING;
						DoMenu(nMenu);
						FormatCard();
						switch (nOperation)
							{
							case MCO_SAVE_GAME:
								//nMenu=MENU_SAVE;
								nMenu=MENU_SAVING;
								DoMenu(nMenu);
								if (SaveData(pData,nBytes))
									nRetVal=MCF_SAVED;
								else
									nRetVal=MCF_FAILED;
								tExit=TRUE;
								break;

							case MCO_LOAD_GAME:
								nMenu=MENU_NO_GAME;
								break;

							case MCO_FRONTEND_CHECK:
								nRetVal=MCF_CAN_SAVE;
								tExit=TRUE;
								break;
							}
						}
					else
						nMenu=MENU_FORMAT;
					break;

				case MENU_CARD_FULL:
				case MENU_NO_GAME:
				case MENU_ERROR:
				case MENU_NO_CARD:
					tExit=TRUE;
					break;

				case MENU_NO_FRONTEND:
				case MENU_NOT_FORMATTED:
					nRetVal=MCF_NONE;
					tExit=TRUE;
					break;

				case MENU_FRONTEND_NEEDS_FORMAT:
					if (nSelection==1)
						nMenu=MENU_FORMAT_CONFIRM;
					else
						{
						nRetVal=MCF_NONE;
						tExit=TRUE;
						}
					break;

				default:
					break;
				}
		}
	CloseEvents();
	DoMenu(MENU_NONE);

	// Ditch 'Pad Removed' text whatever
	T_RemovePrint( padtxt ); padtxt = NULL;
	padremoved = 0;

	return nRetVal;
	}


int S_LoadGame(SAVEGAME_INFO *pData, int nBytes, int level)
{
	int retval;

	padremoved = 0;
	padtxt = NULL;
	prompttxt = NULL;
	camera.number_frames=2;

#ifdef USE_FADES
	if ((Inventory_Mode == INV_TITLE_MODE)
	||  (Inventory_Mode == INV_LEVELSELECT_MODE))
	{
		S_InitFade( FADE_MAX,FADE_MID,0 );		// trigger a fade-down
		S_WaitFade();
	}
#endif

#ifdef GAMEDEBUG
	printf("S_MemoryCardOperation()...");
#endif

	retval = S_MemoryCardOperation(MCO_LOAD_GAME,pData,nBytes);

#ifdef GAMEDEBUG
	printf("Done (retval=%d)\n", retval);
#endif

#if defined(NTSC) && !defined(JAPAN)

	if (retval==MCF_FAILED)
	{
		int lm_count=30*2;
		TEXTSTRING *loadmess = T_Print(0,0,0, GF_PSXStrings[PSSTR_LOADFAIL]);
		T_CentreH(loadmess,1);
		T_CentreV(loadmess,1);
		while(lm_count>0)
		{
			DrawTheScreen();
			lm_count--;
		};
		T_RemovePrint(loadmess); loadmess = NULL;
	}
	else if (retval==MCF_LOADED)
	{
		int lm_count=30*2;
		TEXTSTRING *loadmess = T_Print(0,0,0, GF_PSXStrings[PSSTR_LOADOK]);
		T_CentreH(loadmess,1);
		T_CentreV(loadmess,1);
		while(lm_count>0)
		{
			DrawTheScreen();
			lm_count--;
		};
		T_RemovePrint(loadmess); loadmess = NULL;
	}

#endif

#ifdef USE_FADES
	if ((Inventory_Mode == INV_TITLE_MODE)
	||  (Inventory_Mode == INV_LEVELSELECT_MODE))
	{
		S_InitFade( FADE_MID,FADE_MAX,0 );
		S_WaitFade();
	}
#endif

	reset_flag = 0;
	prompttxt = NULL;

	if ( retval )                              // If says weve loaded then
	{
#ifdef GAMEDEBUG
		printf("Validating CheckSum...");
#endif
		retval = CheckSumValid(pData);         // check the CheckSum
#ifdef GAMEDEBUG
		if (retval)
			printf("OK OK OK OK OK OK OK OK OK OK OK OK \n");
		else
			printf("Failed Failed Failed Failed Failed \n");
#endif
	}

	if (retval)
		GPU_SetScreenPosition(pData->screen_x,pData->screen_y);

	NewAdventureLoadGame = pData->AfterAdventureSave;
	return( retval );
}



int S_SaveGame(SAVEGAME_INFO *pData,int nBytes,int ingame )
{
	int retval;

#if defined(PSX_SONY_DEMO_SHELL) || defined(TEST_SONY_DEMO_SHELL)
	return(1);
#endif
#ifdef GAMEDEBUG
	printf("Savegame size: %d\n", nBytes);
#endif

	padremoved = 0;
	padtxt = NULL;
	prompttxt = NULL;
	camera.number_frames=2;

	if (ingame)
	{
#ifdef USE_FADES
		S_InitFade(FADE_MAX, FADE_MID, 0);
		S_WaitFade();
#endif
	}
	CreateCheckSum( pData );
	retval =  S_MemoryCardOperation(MCO_SAVE_GAME, pData, nBytes);

#if defined(NTSC) && !defined(JAPAN)
	if (retval == MCF_FAILED)
	{
		int lm_count=30*2;
		TEXTSTRING *loadmess = T_Print(0,0,0, GF_PSXStrings[PSSTR_SAVEFAIL]);
		T_CentreH(loadmess,1);
		T_CentreV(loadmess,1);

		while(lm_count > 0)
		{
			DrawTheScreen();
			lm_count--;
		};
		T_RemovePrint(loadmess); loadmess = NULL;
	}
	else if (retval==MCF_SAVED)
	{
		int lm_count=30*2;
		TEXTSTRING *loadmess = T_Print(0,0,0, GF_PSXStrings[PSSTR_SAVEOK]);
		T_CentreH(loadmess,1);
		T_CentreV(loadmess,1);
		while(lm_count>0)
		{
			DrawTheScreen();
			lm_count--;
		};
		T_RemovePrint(loadmess); loadmess = NULL;
	}
#endif

	if ( ingame )
	{
#ifdef USE_FADES
		S_InitFade( FADE_MID,FADE_MAX,0 );
		S_WaitFade();
#endif
	}
	reset_flag = 0;
	if ( prompttxt )
	{
		T_RemovePrint( prompttxt );
		prompttxt = NULL;
	}
	JustSaved = 1;
	return( retval );
}

int S_FrontEndCheck(SAVEGAME_INFO *pData,int nBytes)
{
	int	retval;

#ifdef GAMEDEBUG
	printf("FrontEnd Check...");
#endif
	padremoved = 0;
	padtxt = NULL;
	prompttxt = NULL;
	camera.number_frames=2;
	S_InitFade( FADE_MAX,FADE_MID,0 );				// copy screen into buffer, and trigger a fade-down
	S_WaitFade();

	retval = S_MemoryCardOperation(MCO_FRONTEND_CHECK, pData, nBytes);

	S_InitFade( FADE_MID,FADE_MAX,0 );
	S_WaitFade();
	reset_flag = 0;

	if ( prompttxt )
	{
		T_RemovePrint( prompttxt );
		prompttxt = NULL;
	}

#ifdef GAMEDEBUG
	printf("complete!\n\n");
#endif
	return( retval );
}

#endif
