/*******************************************************************************
 *  The most useful SET5.23 program in the world!
 *  ---------------------------------------------
 *	Thomas Szirtes / Elton Bird   Sega Europe
 *	Version 3 
 *  	Re-engineered for : Set 5.23, KAMUI 1.34
 *
 *	Demonstrates kamui initialization and simple test program
 *  	System malloc and file handling
 *  	Loading of Textures - NEW
 *	Please also look at new modified sbinit.c
 *  	Demonstrates use of new fast kamui macros
 *
 *******************************************************************************/

 
#define		_STRICT_UNION_ 
#define		_KM_USE_VERTEX_MACRO_
#define		_KM_USE_VERTEX_MACRO_L3_

#include	<shinobi.h>
#include	"kamui.h"

#define VBUFFER_SIZE 	8*3*2000

/* new macro stuff - use level 3 */

#include		"kamuix.h"

#include		"file.h"

KMVERTEX3               triangle[4];                           
KMVERTEXBUFFDESC        vertex_buffer;                          
KMVERTEXCONTEXT         global_vc;

KMSURFACEDESC		surface1;				/* surface 1 (front buffer) */
KMSURFACEDESC		surface2;				/* surface 2 (back buffer) */
PKMSURFACEDESC 		psurface[2];			/* array of pointers to surfaces */

/* System Configuration */

KMSYSTEMCONFIGSTRUCT kmsc = {
		sizeof(KMSYSTEMCONFIGSTRUCT),		
		KM_CONFIGFLAG_ENABLE_CLEAR_FRAMEBUFFER,	/* set any special flags = clear frame buffer */
		psurface,						/* pointer to array of surface pointers */
		2,							/* number of buffers = 2 */
		640,480,						/* width and height of surface */
		KM_DSPBPP_RGB565,					/* bits per pixel */
		0x100000,						/* allocate amount of VRAM for texture = 4MB */
		&vertex_buffer,					/* pointer to vertex buffer descriptor */
		0,							/* pointer to vertex buffer - ** malloc now on application side */
		2*VBUFFER_SIZE,							/* size in DWORDS of vertex buffer - must be multiple of 32 */
		{60.f,0.f,40.f,0.f,0.f},			/* percentage of vbuf to use for each type of vertex */
		0,0,0,0,0,0,0,					/* reserved */
		KM_VERTEXBUFMODEL_NORMAL,			/* latency model = 3VB */
	}; 


void definetriangle(KMVERTEX3 *tri)
{
	tri[0].ParamControlWord 	=       KM_VERTEXPARAM_NORMAL;
	tri[0].fX				=       100.f;
	tri[0].fY				=       100.f;

	tri[1].ParamControlWord		=       KM_VERTEXPARAM_NORMAL;	
	tri[1].fX				=       100.f;
	tri[1].fY				=       200.f;
	
	tri[2].ParamControlWord 	=       KM_VERTEXPARAM_NORMAL;
	tri[2].fX				=       200.f;
	tri[2].fY				=       100.f;

	tri[3].ParamControlWord 	=       KM_VERTEXPARAM_ENDOFSTRIP;
	tri[3].fX				=       200.f;
	tri[3].fY				=       200.f;
	
}


void    InitVertexContext(PKMVERTEXCONTEXT pVertexContext)
{
	pVertexContext->RenderState = KM_DEPTHMODE				| KM_CULLINGMODE 
							| KM_SCREENCOORDINATION | KM_SHADINGMODE
							| KM_MODIFIER                   | KM_ZWRITEDISABLE                              
							| KM_SRCBLENDINGMODE    	  | KM_DSTBLENDINGMODE
							| KM_SRCSELECT                  | KM_DSTSELECT
							| KM_FOGMODE                    | KM_USESPECULAR
							| KM_USEALPHA                   | KM_IGNORETEXTUREALPHA
							| KM_FLIPUV                     | KM_CLAMPUV 
							| KM_FILTERMODE                 | KM_SUPERSAMPLE
							| KM_MIPMAPDADJUST              | KM_TEXTURESHADINGMODE
							| KM_PARAMTYPE                  | KM_LISTTYPE                                   
							| KM_COLORTYPE                  | KM_USESPECULAR
							| KM_UVFORMAT;

	pVertexContext->ParamType				= KM_POLYGON;
	pVertexContext->ListType				= KM_OPAQUE_POLYGON;
	pVertexContext->ColorType				= KM_PACKEDCOLOR;
	pVertexContext->bUseSpecular            		= FALSE;
	pVertexContext->UVFormat				= KM_32BITUV;

	pVertexContext->DepthMode				= KM_GREATER;
	pVertexContext->CullingMode				= KM_NOCULLING;
	pVertexContext->ScreenCoordination      		= KM_SCREEN;
	pVertexContext->ShadingMode				= KM_TEXTUREGOURAUD;
	pVertexContext->SelectModifier          		= KM_NOMODIFIER;
	pVertexContext->bZWriteDisable          		= FALSE;                        

	pVertexContext->SRCBlendingMode         		= KM_ONE;
	pVertexContext->DSTBlendingMode         		= KM_ZERO;
	pVertexContext->bSRCSel					= FALSE;                        
	pVertexContext->bDSTSel					= FALSE;                        
	pVertexContext->FogMode					= KM_NOFOG;                     
	pVertexContext->bUseSpecular            		= FALSE;
	pVertexContext->bUseAlpha				= FALSE;
	pVertexContext->FlipUV					= KM_NOFLIP;
	pVertexContext->ClampUV					= KM_NOCLAMP;
	pVertexContext->FilterMode				= KM_POINT_SAMPLE;
	pVertexContext->bSuperSample            		= FALSE;
	pVertexContext->MipMapAdjust            		= 0x00000004;           /* D=1.00 */
	pVertexContext->TextureShadingMode      		= KM_MODULATE;
	

	pVertexContext->bColorClamp             = FALSE;
	pVertexContext->PaletteBank             = 0;

	pVertexContext->fFaceColorAlpha         = 1.0f;         
	pVertexContext->fFaceColorRed           = 1.0f;         
	pVertexContext->fFaceColorGreen         = 1.0f;         
	pVertexContext->fFaceColorBlue          = 1.0f;         
	pVertexContext->fOffsetColorAlpha       = 1.0f;         
	pVertexContext->fOffsetColorRed         = 1.0f;         
	pVertexContext->fOffsetColorGreen       = 1.0f;         
	pVertexContext->fOffsetColorBlue        = 1.0f;         

	kmProcessVertexRenderState(pVertexContext);
}


/**************************************************************
 *      void InitSystem (void)
 *      
 *      Initialize display, buffers and clipping
 **************************************************************/


void InitSystem( void )
{
	KMSTATUS                      ks;
	KMPACKEDARGB			border;

	/* Initialize shinobi - special modified version - will initialize Kamui depending
	   on compiler switch */
 	sbInitSystem(KM_DSPMODE_NTSCNI640x480, KM_DSPBPP_RGB565, 1);
  
	border.dwPacked	=	0x00000000;
	kmSetBorderColor(border);

	psurface[0]			=	&surface1;
	psurface[1]			=	&surface2;
	/* generate vertex buffer */
	kmsc.pVertexBuffer	=	(void *)((((int)syMalloc(2*sizeof(KMDWORD)*VBUFFER_SIZE)&0x3FFFFFE0)|0xA0000000)+32);  

	/* Set system configuration */
	ks		=	kmSetSystemConfiguration(&kmsc);			
  
	InitVertexContext(&global_vc);

	ks      =       kmSetVertexRenderState(&global_vc);
	if (ks!=KMSTATUS_SUCCESS) {  return; }

	ks      =       kmSetBackGroundRenderState(&global_vc);
	if (ks!=KMSTATUS_SUCCESS) {  return; }
}


/**************************************************************
 *      void QuitSystem (void)
 *      
 **************************************************************/

void QuitSystem( void )
{
 	syFree	(kmsc.pVertexBuffer); 

	sbExitSystem();
}



/**************************************************************************
 *
 * void  LoadTexture (char *filename , PKMSURFACEDESC pTexSurfaceDesc)    
 *
 * Uses File access functions in File.c
 *
 *************************************************************************/

void	LoadTexture(char *filename , PKMSURFACEDESC pTexSurfaceDesc)
{
	Uint32		n;
	int			t;	
	GDFS			gdfs;
	long			filelen;
	DWORD			nSize;
	PKMDWORD		pTexture;
	KMSTATUS		status;
	KMTEXTURETYPE	nTextureType;

	/* Open file - allocate memory and load*/ 
	
	gdfs  =  t_fopen( filename, NULL );      		/* Open File     */
	gdFsGetFileSize(gdfs, &filelen);			/* length of texture file*/
	pTexture	=	(PKMDWORD)syMalloc(filelen);	/* allocate main memory*/
	t_fread((Uint8 *)pTexture, filelen,1,gdfs);	/* read in texture file*/
	t_fclose(gdfs);						/* file close*/
		
	/* parse GBIX - cheating not checking chunk sizes */
	if(pTexture[0]!=0x58494247)
	{
		t=0;
	}
	else
	{
		t=3;
	}

	/* parse PVRT  */
	if(pTexture[t]!=0x54525650)
	{
		syFree(pTexture);
		return;
	}
	
	nTextureType	=		pTexture[t+2];
	nSize			=		pTexture[t+3];

	status	=	kmCreateTextureSurface(
								pTexSurfaceDesc,
								(nSize&0xffff), 
								((nSize>>16)&0xffff), 
								nTextureType	
							  	);
			
	 
	status	=	kmLoadTexture(
							pTexSurfaceDesc,
							pTexture + (16 / sizeof(KMDWORD)) + t,			
							FALSE,		
							FALSE			
							);
	syFree(pTexture);
}



void checkfile(int *buffer,int b_length)
{
	int n;
	for (n=0;n<(b_length/4);n++)
	{
		if (buffer[n]!=n)
		{	
			while(1);
		}
	}
}


/**************************************************************
 *      void main( int argc, char *argv[ ], char *envp[ ] )
 *      
 **************************************************************/
const PDS_PERIPHERAL    *per;

Uint8	*buffer1;
Uint8	*buffer2;
Uint8	*buffer3;

void main( void )
{
	KMSTATUS                      ks;
	int                           frame=0,n,m;
	GDFS                          fd;
	KMSURFACEDESC			texture1;
	KMPACKEDARGB			col;

	col.dwPacked	=	0xffffffff;

	InitSystem();

	definetriangle(triangle);


	LoadTexture("Starfish.pvr",&texture1);

	global_vc.pTextureSurfaceDesc	=	&texture1;
	kmProcessVertexRenderState	(&global_vc);


	/* main loop */
	while( TRUE )       
	{
  		/* now you can get the peripheral data for the desired port */
 		per = pdGetPeripheral(PDD_PORT_A0); 

		/* this checks which button is pressed - just like Ninja */

	if (per->on & PDD_DGT_KL) 
		{
			triangle[0].fX  -=      1.0f;
			triangle[1].fX  -=      1.0f;
			triangle[2].fX  -=      1.0f;
			triangle[3].fX  -=      1.0f;
		}

		if (per->on & PDD_DGT_KU) 
		{
			triangle[0].fY  -=      1.0f;
			triangle[1].fY  -=      1.0f;
			triangle[2].fY  -=      1.0f;
			triangle[3].fY  -=      1.0f;
		}

		if (per->on & PDD_DGT_KD) 
		{
			triangle[0].fY  +=      1.0f;
			triangle[1].fY  +=      1.0f;
			triangle[2].fY  +=      1.0f;
			triangle[3].fY  +=      1.0f;
		}

		if (per->on & PDD_DGT_KR) 
		{
			triangle[0].fX  +=      1.0f;
			triangle[1].fX  +=      1.0f;
			triangle[2].fX  +=      1.0f;
			triangle[3].fX  +=      1.0f;

		}


		/* Set our frame render state */
		ks = kmSetVertexRenderState(&global_vc);

		/* new macro l3 to set a triangle down */
		kmxxGetCurrentPtr(&vertex_buffer)
		kmxxStartVertexStrip(&vertex_buffer)

/*
	 			kmxxSetVertex_3( KM_VERTEXPARAM_NORMAL,
	 						     triangle[1].fX+n, triangle[1].fY+m, 1.f, 
	 						     0.0f, 1.0f, 
	 						     col, col )
*/  
		((PKMVERTEX3)(pkmCurrentPtr))->ParamControlWord	= KM_VERTEXPARAM_NORMAL;					\
  		((PKMVERTEX3)(pkmCurrentPtr))->fX				= triangle[0].fX;					
		((PKMVERTEX3)(pkmCurrentPtr))->fY				= triangle[0].fY;
		((PKMVERTEX3)(pkmCurrentPtr))->u.fZ				= 1.0f;				
		((PKMVERTEX3)(pkmCurrentPtr))->fU				= 0.f;					
		((PKMVERTEX3)(pkmCurrentPtr))->fV				= 0.f;					
		((PKMVERTEX3)(pkmCurrentPtr))->uBaseRGB			= col;					
		((PKMVERTEX3)(pkmCurrentPtr))->uOffsetRGB		= col;					
		prefetch((void *)pkmCurrentPtr);										
		pkmCurrentPtr += (sizeof(KMVERTEX3) / sizeof(KMDWORD));

/*
	 			kmxxSetVertex_3( KM_VERTEXPARAM_NORMAL,
	 						     triangle[1].fX+n, triangle[1].fY+m, 1.f, 
	 						     0.0f, 1.0f, 
	 						     col, col )
*/  
		((PKMVERTEX3)(pkmCurrentPtr))->ParamControlWord	= KM_VERTEXPARAM_NORMAL;					\
  		((PKMVERTEX3)(pkmCurrentPtr))->fX				= triangle[1].fX;					
		((PKMVERTEX3)(pkmCurrentPtr))->fY				= triangle[1].fY;
		((PKMVERTEX3)(pkmCurrentPtr))->u.fZ				= 1.0f;				
		((PKMVERTEX3)(pkmCurrentPtr))->fU				= 0.f;					
		((PKMVERTEX3)(pkmCurrentPtr))->fV				= 1.f;					
		((PKMVERTEX3)(pkmCurrentPtr))->uBaseRGB			= col;					
		((PKMVERTEX3)(pkmCurrentPtr))->uOffsetRGB		= col;					
		prefetch((void *)pkmCurrentPtr);										
		pkmCurrentPtr += (sizeof(KMVERTEX3) / sizeof(KMDWORD));

/*
	 			kmxxSetVertex_3( KM_VERTEXPARAM_NORMAL,
	 						     triangle[1].fX+n, triangle[1].fY+m, 1.f, 
	 						     0.0f, 1.0f, 
	 						     col, col )
*/  
		((PKMVERTEX3)(pkmCurrentPtr))->ParamControlWord	= KM_VERTEXPARAM_NORMAL;					\
  		((PKMVERTEX3)(pkmCurrentPtr))->fX				= triangle[2].fX;					
		((PKMVERTEX3)(pkmCurrentPtr))->fY				= triangle[2].fY;
		((PKMVERTEX3)(pkmCurrentPtr))->u.fZ				= 1.0f;				
		((PKMVERTEX3)(pkmCurrentPtr))->fU				= 1.f;					
		((PKMVERTEX3)(pkmCurrentPtr))->fV				= 0.f;					
		((PKMVERTEX3)(pkmCurrentPtr))->uBaseRGB			= col;					
		((PKMVERTEX3)(pkmCurrentPtr))->uOffsetRGB		= col;					
		prefetch((void *)pkmCurrentPtr);										
		pkmCurrentPtr += (sizeof(KMVERTEX3) / sizeof(KMDWORD));

/*
	 			kmxxSetVertex_3( KM_VERTEXPARAM_ENDOFSTRIP, 
	 							 triangle[3].fX+n, triangle[3].fY+m, 1.f, 
	 							 1.0f, 1.0f, 
	 							 col, col )
*/  

		((PKMVERTEX3)(pkmCurrentPtr))->ParamControlWord	= KM_VERTEXPARAM_ENDOFSTRIP;					\
  		((PKMVERTEX3)(pkmCurrentPtr))->fX				= triangle[3].fX;					
		((PKMVERTEX3)(pkmCurrentPtr))->fY				= triangle[3].fY;
		((PKMVERTEX3)(pkmCurrentPtr))->u.fZ				= 1.0f;				
		((PKMVERTEX3)(pkmCurrentPtr))->fU				= 1.f;					
		((PKMVERTEX3)(pkmCurrentPtr))->fV				= 1.f;					
		((PKMVERTEX3)(pkmCurrentPtr))->uBaseRGB			= col;					
		((PKMVERTEX3)(pkmCurrentPtr))->uOffsetRGB		= col;					
		prefetch((void *)pkmCurrentPtr);										
		pkmCurrentPtr += (sizeof(KMVERTEX3) / sizeof(KMDWORD));

		kmxxReleaseCurrentPtr(&vertex_buffer);


		/* go away and render */
  		kmRender();

		kmFlipFrameBuffer();
		/* must call this straight after flip frame buffer */
 		pdExecPeripheralServer(); 

   	}

      ks      =       kmDiscardVertexBuffer(&vertex_buffer);
	
	/*	Free memory */

	QuitSystem();

}
