// ----------------------------------------------------------------------------
// DS Frotz
// 	
// description	:	DS version of GBA Frotz 
//					made with PAlib - www.palib.com -
//
// ----------------------------------------------------------------------------
// (c) 2006 papafuji - using PAlib
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
// PAlib includes
// ----------------------------------------------------------------------------

#include <PA9.h>
#include <stdio.h> 

// ----------------------------------------------------------------------------
// project includes
// ----------------------------------------------------------------------------

#include "frotz.h"

#include "main.h"
#include "draw.h"
#include "sound.h"

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

#include "gfx/all_gfx.h"


// ----------------------------------------------------------------------------
// CONSTANTS
// ----------------------------------------------------------------------------

#define WAIT_CR					REG_EXEMEMCNT

#define	SRAM_SIZE				65536

#define DATA_BLOCK_START		512
#define DATA_BLOCK_SIZE			5024

#define START_OFFSET( _i_ )		( DATA_BLOCK_START + ((_i_) * DATA_BLOCK_SIZE ) )

#define SAVEDATA_HEADER_START	"START_DSFROTZ_DATA"
#define SAVEDATA_HEADER_END		"END_DSFROTZ_DATA"

// ---------------------------------------------------------------------------
// DRAWING CONSTANTS
// ---------------------------------------------------------------------------

#define LINE_X					4
#define TITLE_Y					4

#define FIRST_LINE_Y			18
#define LINE_HEIGHT				24
#define LAST_LINE_Y				(FIRST_LINE_Y +(nbFilesMax+1)*LINE_HEIGHT)

#define LINE_INDEX(_i_)			(((_i_) - FIRST_LINE_Y) / LINE_HEIGHT)

#define EXIT_START_Y			180
#define EXIT_END_Y				(EXIT_START_Y+12)


// ----------------------------------------------------------------------------
// local variables
// ----------------------------------------------------------------------------

char 		desc[256];
char		gameName[80];
char		buf[4096];

// ---------------------------------------------------------------------------
// EXTERNALS
// ---------------------------------------------------------------------------

extern int 						numActions;
extern int 						z_save_DS	( unsigned char* _buffer, int _start_offset );
extern int 						z_restore_DS( unsigned char* _buffer, int _start_offset );

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

void GetScoreAndMoves( int* _score, int* _moves )
{
    zword global0;
    zword global1;
    zword global2;
    zword addr;

    /* One V5 game (Wishbringer Solid Gold) contains this opcode by
       accident, so just return if the version number does not fit */

	// DS Frotz patch - i just remove this test - it works sometimes...
    if (h_version >= V4)
	{
		*_score = (int)-1;
		*_moves = (int)-1;
		return;
	}
	// end of DS Frotz patch - 

    /* Read all relevant global variables from the memory of the
       Z-machine into local variables */

    addr = h_globals;
    LOW_WORD (addr, global0)
    addr += 2;
    LOW_WORD (addr, global1)
    addr += 2;
    LOW_WORD (addr, global2)

	*_score = (int)global1;
	*_moves = (int)global2;
	
	//WAIT_MSG2( "GET SCORE %d - MOVES %d", *_score, *_moves );
}

// ----------------------------------------------------------------------------
// compare two bytes buffers until a '\0'
// ----------------------------------------------------------------------------

int CompareBytes( char* _s0, char* _s1 )
{
	while( *_s0 )
	{
		if ( *_s0 != *_s1 )
		{
			return false;
		}
		_s0 ++;
		_s1 ++;
	}
	return true;
}

// ----------------------------------------------------------------------------
// write data from a buffer to another :
//
//		if _nbbytes is < 0, 	_src is considered as a string
//		elsewhere				_src is considered as a byte buffer
// ----------------------------------------------------------------------------

int WriteBytes( unsigned char* _dest, char* _src, int _nbbytes )
{
	int		len = (_nbbytes<0) ? (strlen( _src )+1) : _nbbytes;

	memcpy( _dest, _src, len );

	return len;
}

// ----------------------------------------------------------------------------
// GET FILE DESCRIPTION - 
//
//		file may be in SRAM or in a file
// ----------------------------------------------------------------------------

int GetFileDescription( int _fileIndex,  char* _gameName, char* _desc )
{
	char*	data	 	= NULL;
	int 	offset		= 0;

	if ( _desc ) 		strcpy( _desc, 		"empty" );
	if ( _gameName )	strcpy( _gameName,	"" 		);
	
	if ( gUseFat && gFatStatus )
	{
		data 	= PF_ReadSAVFile( _fileIndex, NULL );
//DBG_PRINT( "GetDesc %d: FROM FILE %s", _fileIndex  );
	}
	else
	{
		WAIT_CR &= ~0x80;
		data 	= (char*)SRAM + START_OFFSET( _fileIndex );
//DBG_PRINT( "GetDesc %d: COMPARE SRAM DATA %s (%d)", _fileIndex, SAVEDATA_HEADER_START, offset );
	}

	if ( data == NULL )
	{
if ( _desc && gDSdebugMode )	strcpy( _desc, 		"DATA = NULL - no data to read" );
		offset 		= -1;
	}
	// first, read the SAVEDATA HEADER... to be sure...
	else if ( !CompareBytes( SAVEDATA_HEADER_START, data+offset ) )
	{
if ( _desc && gDSdebugMode )	strcpy( _desc, 		"BAD HEADER" );
//DBG_PRINT( "GetDesc %d: BAD HEADER", _fileIndex );
		offset 		= -1;
	}
	else
	{
		offset += strlen( SAVEDATA_HEADER_START ) + 1;
	
		// then, read the saved game name... to be sure...
		if ( !CompareBytes( gGameName, data+offset ) )
		{
if ( _desc && gDSdebugMode ) strcpy( _desc, 		"BAD GAME NAME " ); 
//DBG_PRINT( "GetDesc %d: BAD GAME NAME", _fileIndex );
			offset = -1;
		}
		else
		{
			if ( _gameName )	strcpy( _gameName, data+offset );
			offset += strlen( gGameName ) + 1;

			// then, read the game description...
			offset += WriteBytes( (unsigned char*)buf, data+offset, -1 );
		//DBG_PRINT2( "GetDesc %d: READ DESC %s", _fileIndex, buf );
	
			if ( !strstr( buf, "DESC:" ) )
			{
	if ( _desc && gDSdebugMode ) strcpy( _desc, 		"BAD DESCRIPTION" ); 
				offset = -1;
			}
			else
			{
				if ( _desc )
				{
					strcpy( _desc, buf + 5 );
				}
			}
		}
	}

	if ( gUseFat && gFatStatus && data )
	{
		free( data );
	}

	return offset;
}
/*
int GetBlockDescription( int _blocIndex,  char* _gameName, char* _desc )
{
	int 	offset 				= START_OFFSET( _blocIndex );
	
	//WAIT_CR &= ~0x80;

	if ( _desc ) 		strcpy( _desc, 		"empty" );
	if ( _gameName )	strcpy( _gameName,	"" 		);
	
	if ( !SearchString( SAVEDATA_HEADER_START, offset ) )
	{
		return -1;
	}
	offset += PA_LoadString( offset, buf )+1;

	if ( !strcmp( buf, SAVEDATA_HEADER_START ) )
	{
		offset += PA_LoadString( offset, buf )+1;
		if ( _gameName )
		{
			strcpy( _gameName, buf ); 
		}

		offset += PA_LoadString( offset, buf )+1;
		if ( strstr( buf, "DESC:" ) )
		{
			if ( _desc )
			{
				strcpy( _desc, buf + 5 );
			}
			return offset;
		}
	}

	return -1;
}
*/

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// choose a File in the files list (SRAM or cart)
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------

int PickFile( char* _title, char *_bottom )
{
	int 	i, redrawList		= 1;
	int		blocIndex 			= -1;
	int		nbFilesMaxOnScreen 	= (int)(192/LINE_HEIGHT) - 1;
	int		firstFileIndex		= 0;
	int		nbFilesMax			= ( gUseFat && gFatStatus ) 	? NB_FILES_MAX_ON_CART
																: NB_FILES_MAX_IN_SRAM;	

	PF_ParseSAVFiles();

	HideSprites		();
	PA_DeleteBg		( BOTTOM_SCREEN, TILED_BG_INDEX );

	while( true )
	{
		if ( redrawList )
		{
			FILL_SCREEN( BOTTOM_SCREEN, 17 );
			PRINT_STRING( BOTTOM_SCREEN, _title, 					LINE_X, TITLE_Y, 	33, 7, -1 );
	
			for( i = 0; i < nbFilesMaxOnScreen; i++ )
			{
				GetFileDescription( i+firstFileIndex, gameName, desc );
				sprintf( buf, "[31]file %d -[28] %s - %s", i+firstFileIndex+1, 	gameName, desc );
				PRINT_STRING	( BOTTOM_SCREEN,  buf, 				LINE_X+8, FIRST_LINE_Y+i*LINE_HEIGHT, 31, 7, -1 );
			}
			PRINT_STRING	( BOTTOM_SCREEN, _bottom, 				LINE_X, EXIT_START_Y, 	33, 7, -1 );
			PA_WaitForVBL();
			redrawList = false;
		}

		if ( Stylus.Newpress )
		{
			if ( ( Stylus.Y >= EXIT_START_Y && Stylus.Y <= EXIT_END_Y ) || ( Stylus.Y >= FIRST_LINE_Y && Stylus.Y <= LAST_LINE_Y ) )
			{
				PA_WaitForVBL(); while( Stylus.Held ) PA_WaitForVBL();
				if ( Stylus.Y >= EXIT_START_Y && Stylus.Y <= EXIT_END_Y )
				{
					break;
				}
				blocIndex = LINE_INDEX(Stylus.Y) + firstFileIndex;
				if ( blocIndex < 0 || blocIndex >= nbFilesMax )
				{
					blocIndex = -1;
//DBG_PRINT( "blocIndex %d", blocIndex );
				}
				break;
			}
		}
		else if ( gUseFat && gFatStatus )
		{
			if ( Pad.Newpress.L ) 
			{
				if ( firstFileIndex > 0 )
				{
					firstFileIndex --;
					redrawList = 1;
				}
			}
			else if ( Pad.Newpress.R ) 
			{
				if ( firstFileIndex < (nbFilesMax-nbFilesMaxOnScreen) )
				{
					firstFileIndex ++;
					redrawList = 1;
				}
			}
		}
		
		PA_WaitForVBL();
	}

	PA_WaitForVBL();
	return blocIndex;
}

/*
int PickFile( char* _title, char *_bottom )
{
	int 	i, blocIndex = -1;
	
	//WAIT_CR &= ~0x80;

	HideSprites		();
	PA_DeleteBg		( BOTTOM_SCREEN, TILED_BG_INDEX );
	FILL_SCREEN( BOTTOM_SCREEN, 17 );
	PRINT_STRING( BOTTOM_SCREEN, _title, 					LINE_X, TITLE_Y, 	33, 7, -1 );

	for( i = 0; i < NB_BLOCKS_MAX; i++ )
	{
		GetBlockDescription( i, gameName, desc );
		sprintf( buf, "[31]- file %d -[28] %s - %s", i+1, 	gameName, desc );
		PRINT_STRING	( BOTTOM_SCREEN,  buf, 				LINE_X, FIRST_LINE_Y+i*LINE_HEIGHT, 31, 7, -1 );
	}

	PRINT_STRING	( BOTTOM_SCREEN, _bottom, 				LINE_X, EXIT_START_Y, 	33, 7, -1 );
	PA_WaitForVBL();

	while( true )
	{
		if ( Stylus.Newpress )
		{
			if ( ( Stylus.Y >= EXIT_START_Y && Stylus.Y <= EXIT_END_Y ) || ( Stylus.Y >= FIRST_LINE_Y && Stylus.Y <= LAST_LINE_Y ) )
			{
				PA_WaitForVBL(); while( Stylus.Held ) PA_WaitForVBL();
				if ( Stylus.Y >= EXIT_START_Y && Stylus.Y <= EXIT_END_Y )
				{
					break;
				}
				blocIndex = LINE_INDEX(Stylus.Y);
				if ( blocIndex < 0 || blocIndex >= NB_BLOCKS_MAX )
				{
					blocIndex = -1;
//DBG_PRINT( "blocIndex %d", blocIndex );
				}
				break;
			}
		}
		PA_WaitForVBL();
	}

	PA_WaitForVBL();
	return blocIndex;
}

*/


// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------

int SaveGame( char* ____gameName,int _fileIndex, int _nbBytes )
{
	int 				i;
	unsigned char*	data	 		= NULL;
	int					offset 			= 0;
	int					nbFilesMax		= ( gUseFat && gFatStatus ) 	? NB_FILES_MAX_ON_CART
															: NB_FILES_MAX_IN_SRAM;	
	
	if ( !gGameName || !gGameName[0] || (_fileIndex<0) || (_fileIndex>=nbFilesMax) )
	{
		PRINT_STRING	( 0, "SAVE FAILED", 	12, 100, 31, 7, -12 );
		return -1;
	}

	if ( gUseFat && gFatStatus )
	{
DBG_MSG2( "SaveGame %d : START WRITE %d bytes ON CART", _fileIndex, _nbBytes );
		data 	= (unsigned char*)malloc( (_nbBytes+1024) * sizeof(char));
	}
	else
	{
DBG_MSG( "SaveGame %d : WRITE FILE IN SRAM", _fileIndex );
		WAIT_CR &= ~0x80;
		data 	= (unsigned char*)SRAM + START_OFFSET( _fileIndex );
		for( i=0; i<DATA_BLOCK_SIZE; i++ )
		{
			data[i] = 0;
		}
	}

	if ( data )
	{
		DBG_MSG3( "SaveGame %d :  %d HEADER %s", _fileIndex, offset, SAVEDATA_HEADER_START );
			offset += WriteBytes( data + offset, SAVEDATA_HEADER_START,	-1		);
	
	//DBG_PRINT( "START SAVE GAME @ %d ", offset );
	
		//offset += PA_SaveString( offset, gGameName )+1;
		offset += WriteBytes( data + offset, gGameName,	-1		);
	
		if ( strstr( gStatusChars, "S:") == NULL && strstr( gStatusChars, "M:") == NULL && strstr( gStatusChars, "Time:") == NULL )
		{
			int 	score, moves;
			GetScoreAndMoves( &score, &moves );
			if ( score > -1 && moves > -1 )
				sprintf( buf, "DESC:%s - S:%d M:%d", gStatusChars, score, moves );
			else
				sprintf( buf, "DESC:%s", gStatusChars );
		}
		else
		{
			sprintf( buf, "DESC:%s", gStatusChars );
		}
		
		//offset += PA_SaveString( offset, buf )+1;
		offset += WriteBytes( data + offset,  buf,	-1 );
	
		int 	nbBytesWritten = z_save_DS( data, offset );
	
		offset += nbBytesWritten;
	
		if ( gUseFat && gFatStatus && data )
		{
			_nbBytes = offset;
			PF_WriteSAVFile( _fileIndex, (char*)data, _nbBytes );
			free( data );
		}
	}
	else
	{
		_nbBytes = -1;
		DBG_MSG2( "SaveGame %d : SUCCESSFULL (%d bytes)", _fileIndex, offset );
	}

DBG_MSG( "SAVE GAME - SUCCESSFULL %d", 2 );

	return 2;
}

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------

int LoadGame( char* _gameName, int _fileIndex )
{
	unsigned char*	data 		= NULL;
	int 				offset 		= 0;
	int					nbFilesMax	= ( gUseFat && gFatStatus ) 	? NB_FILES_MAX_ON_CART
																	: NB_FILES_MAX_IN_SRAM;	

	if ( !_gameName || !_gameName[0] || (_fileIndex<0) || (_fileIndex>=nbFilesMax) )
		return 1;	// return to game

	if ( gUseFat && gFatStatus )
	{
		data	= (unsigned char*)PF_ReadSAVFile( _fileIndex, NULL );
	}
	else
	{
		WAIT_CR &= ~0x80;
		data 	= (unsigned char*)SRAM + START_OFFSET( _fileIndex );
	}

	if ( data == NULL )
	{
		PRINT_STRING	( 0, "LOAD FAILED - NULL FILE DATA", 	12, 108, 31, 7, -12 );
		return -1;
	}
	else if ( (offset = GetFileDescription( _fileIndex,  gameName, NULL )) < -1 )
	{
		PRINT_STRING	( 0, "LOAD FAILED - BAD BLOC DESC", 	12, 108, 31, 7, -12 );
		return -1;
	}
	else if ( strcmp( gameName, _gameName ) )
	{
		PRINT_STRING	( 0, "LOAD FAILED - BAD GAME NAME", 	12, 108, 31, 7, -12 );
		return -1;
	}

	int 	result = z_restore_DS( data, offset );

	if ( gUseFat && gFatStatus && data )
	{
		free( data );
	}

	if ( result < 0 )
	{
DBG_PRINT( "LoadGame %d : FAILED", _fileIndex );
	}
	else
	{
DBG_PRINT( "LoadGame %d : SUCCESSFULL", _fileIndex );
	}

	return result;
}


// ----------------------------------------------------------------------------
// 
// ----------------------------------------------------------------------------

int Save( int _nbBytes )
{
	int		result = 0;
	
	HideSprites		();
	PA_DeleteBg		( BOTTOM_SCREEN, TILED_BG_INDEX );
	
	FILL_SCREEN		( BOTTOM_SCREEN, 17 );
	
	int 	fileIndex = PickFile( "save a game ?", "return to game" );
	
	result = SaveGame( gGameName, fileIndex, 16384 );

	switch ( gInputMode )
	{
	case INPUT_MODE_GRAFITI 	: PA_LoadTiledBg	( BOTTOM_SCREEN, TILED_BG_INDEX, bottom );  break;
	case INPUT_MODE_KEYBOARD	: PA_LoadTiledBg	( BOTTOM_SCREEN, TILED_BG_INDEX, keybd ); break;
	case INPUT_MODE_NUMPAD		: PA_LoadTiledBg	( BOTTOM_SCREEN, TILED_BG_INDEX, numpad ); break;
	}
	
	PA_WaitForVBL();
	
	ShowSprites		();
	RefreshScreens	( 3 );

	return result;
}

// ----------------------------------------------------------------------------
// 
// ----------------------------------------------------------------------------

int Restore( void )
{
	int		result = 0;
	
	HideSprites		();
	PA_DeleteBg		( BOTTOM_SCREEN, TILED_BG_INDEX );
	
	FILL_SCREEN		( BOTTOM_SCREEN, 17 );

	int 	fileIndex = PickFile( "restore a saved game ?", "return to game" );
	
	result = LoadGame( gGameName, fileIndex );
	
	switch ( gInputMode )
	{
	case INPUT_MODE_GRAFITI 	: PA_LoadTiledBg	( BOTTOM_SCREEN, TILED_BG_INDEX, bottom );  break;
	case INPUT_MODE_KEYBOARD	: PA_LoadTiledBg	( BOTTOM_SCREEN, TILED_BG_INDEX, keybd ); break;
	case INPUT_MODE_NUMPAD		: PA_LoadTiledBg	( BOTTOM_SCREEN, TILED_BG_INDEX, numpad ); break;
	}
	
	PA_WaitForVBL();
	
	ShowSprites		();
	RefreshScreens	( 3 );

	return result;
}

// ----------------------------------------------------------------------------
// (c) 2006 papafuji - using PAlib
// ----------------------------------------------------------------------------
