/*============================================================================
  NuSYSTEM sample program SNAKE TAIL HACK
  
  main.c
  
  Copyright (C) 1997, NINTENDO Co,Ltd.
  ============================================================================*/

#include <nusys.h>
#include <nualsgi_n.h>

#include "main.h"
#include "para.h"


/* The structure for creating Controller trigger data */
NUContData ContTriger[NU_CONT_MAXCONTROLLERS];
NUContPakFile*	file_chk;

/*  For game replay */
ContTrace ConTrace[100*60][4];
int TraceIdx;
int ReplayMode;

/* The buffer for saving the Controller data */
u8 SaveBuf[100*60*4];
int SaveIdx[4];
int SaveOfsIdx;

int ContPakError;
int ContPakOpen[4];
int ContPakFileOpen[4];

/* The stage number*/
volatile int stage;

/* Declaration of the prototype  */
void gamemain(int);
void gametitle(int);
void gameover(int);
void pakerr(int);
void pakrwerr(int);
void SavePadData( void );
void ReplayPadData( void );
void SaveContPak( void );
void LoadContPak( void );
void ContPakOpenAll( void );
void NoteOpenAll( void );
int ChkPakStat( void );


/* Declaration of extern */
/* gamemain.c */
extern void makegameDL(void);
extern void updateGame(void);
extern void InitSnake( void );
extern void InitShotTail( void );
extern float View;



/* gametitle.c */
extern void maketitleDL(void);
extern void updateTitle(void);

/* gameover.c */
extern void makegameoverDL(void);
extern void updateGameOver(void);

/* gamepakerr.c */
extern void makepakerrDL(void);
extern void updatePakErr(void);
extern int NoSave;

/* gameparwkerr.c */
extern void makepakrwerrDL(void);
extern void updateRwPakErr(void);

/* main.c */
extern int Pause;

/* For n_audio */
extern u8 n_aspMainTextStart[],n_aspMainTextEnd[];
extern u8 n_aspMainDataStart[],n_aspMainDataEnd[];

extern u8 _midibankSegmentRomStart[],_midibankSegmentRomEnd[];
extern u8 _miditableSegmentRomStart[],_miditableSegmentRomEnd[];
extern u8 _seq1SegmentRomStart[],_seq1SegmentRomEnd[];

extern u8 _sfxbankSegmentRomStart[],_sfxbankSegmentRomEnd[];
extern u8 _sfxtableSegmentRomStart[],_sfxtableSegmentRomEnd[];

NUContPakFile pakFile[4];

/*----------------------*/
/*   The main process  	*/
/*  IN:	None		*/
/*  RET:None		*/
/*----------------------*/
void
mainproc(void)
{
    extern ALSynConfig    auSynConfig;

    /* The initialization of graphic*/
    nuGfxInit();

    /* The initialization of the controller manager*/
    nuContInit();

    /* The initialization of the audio manager */
    nuAuInit();

    /*Register the bank to the sound player */
    nuAuSndPlayerBankSet(_sfxbankSegmentRomStart,
			 _sfxbankSegmentRomEnd - _sfxbankSegmentRomStart,
			 _sfxtableSegmentRomStart
			 );
    
    /*Register the bank to the sequence player */
    nuAuSeqPlayerBankSet(_midibankSegmentRomStart,
			 _midibankSegmentRomEnd - _midibankSegmentRomStart,
			 _miditableSegmentRomStart
			 );

    /*Register the sequence bank to the sequence player*/
    nuAuSeqPlayerSeqSet(_seq1SegmentRomStart);

    /* Set the stage number to 0*/
    stage = 0;

    /* Select the call-back routine depending on stage numbers. 
        The call-back sets the value to stage for specifying the call-back
       routine registering next when stage ends.*/

    InitSnake();
    InitShotTail();
    View = 0.0;
    Pause = 1;
    TraceIdx = 0;
    SaveIdx[0] = 0;
    SaveIdx[1] = 0;
    SaveIdx[2] = 0;
    SaveIdx[3] = 0;
    SaveOfsIdx = 0;
    ReplayMode = 0;
    while(1){
	switch(stage){
	  case 0:/*Title*/
	    /* Set stage to the different value from the stage number first so
	       that you can notice the call-back has set the value */
	    stage = -1;
	    /* Play sequence of the title */
	    nuAuSeqPlayerSetNo(0,0);
	    nuAuSeqPlayerPlay(0);
	    /* Call-back register */
	    nuGfxFuncSet((NUGfxFunc)gametitle);
	    /* Start to display*/
	    nuGfxDisplayOn();
	    break;
	  case 1:/*Main of the game*/
	    stage = -1;
	    /* Play sequence of this game volume */
	    nuAuSeqPlayerSetNo(0,2);
	    nuAuSeqPlayerPlay(0);
	    /* Call-back register*/
	    nuGfxFuncSet((NUGfxFunc)gamemain);
	    /* Start to display*/
	    nuGfxDisplayOn();
	    break;
	  case 2:/*Game-over */
	    stage = -1;
	    /* Play sequence at game-over  */
	    nuAuSeqPlayerSetNo(0,1);
	    nuAuSeqPlayerPlay(0);
	    /* Call-back register */
	    nuGfxFuncSet((NUGfxFunc)gameover);
	    /* Start to display*/
	    nuGfxDisplayOn();
	    break;
	  case 3:/*Pack error*/
	    stage = -1;
	    /* Call-back register  */
	    nuGfxFuncSet((NUGfxFunc)pakerr);
	    /* Start to display */
	    nuGfxDisplayOn();
	    break;
	  case 4:/*Pack error*/
	    stage = -1;
	    /* Call-back register  */
	    nuGfxFuncSet((NUGfxFunc)pakrwerr);
	    /*Start to display*/
	    nuGfxDisplayOn();
	    break;
	  default:
	    break;
	}
      
	/*  Wait for switching the scene  */
	while(stage == -1);
	/* Clear the display */
	nuGfxDisplayOff();
    }
}

  
/*----------------------*/
/*  The title process 	*/
/*  IN:	pendingGfx	*/
/*  RET:None		*/
/*----------------------*/
void
gametitle(int pendingGfx)
{
    /* Provide the display process if there is only one task which is processing or waiting for the process.*/
    if(pendingGfx < 2){
	maketitleDL();		
	/*Create trigger data*/
	nuContDataGetExAll(&ContTriger[0]);
    
	/* The title progressing process */
	updateTitle();
    }
}

/*------------------------------*/
/*  The process of game main 	*/
/*  IN:	pendingGfx		*/
/*  RET:None			*/
/*------------------------------*/
void
gamemain(int pendingGfx)
{
    /* Provide the display process if there is only one task which is processing or waiting for the process. */
    if(pendingGfx < 2){
	makegameDL();		

	/*In the case of selecting the REPLAY MODE at the title*/
	if(ReplayMode){
	    ReplayPadData();
	    Pause = 0;
	}
	/*Create trigger data*/
	nuContDataGetExAll(&ContTriger[0]);
	
	/*In the case of selecting PADDATA SAVE at the title*/
	if(NoSave == 0){
	    if(ReplayMode == 0 && Pause == 0){
		SavePadData();
	    }
	}
    
	/* The game progressing process */
	updateGame();
    }
}

/*------------------------------*/
/*  The process of game-over  	*/
/*  IN:	pendingGfx		*/
/*  RET:None			*/
/*------------------------------*/
void
gameover(int pendingGfx)
{
    /* Provide the display process if there is only one task which is processing or waiting for the process. */
    if(pendingGfx < 2){
	makegameoverDL();

	/*Create trigger data*/
	nuContDataGetExAll(&ContTriger[0]);
    
	/* The game-over progressing process */
	updateGameOver();
    }
}


/*----------------------------------------------*/
/*  The Controller Pak error process 		*/
/*  IN:	pendingGfx				*/
/*  RET:None					*/
/*----------------------------------------------*/
void
pakerr(int pendingGfx)
{
    /* Provide the display process if there is only one task which is processing or waiting for the process. */
    if(pendingGfx < 2){
	makepakerrDL();

	/*Create tigger data*/
	nuContDataGetExAll(&ContTriger[0]);
    
	/* The game-over progressing process*/
	updatePakErr();
    }
}


/*----------------------------------------------*/
/*  The Controller Pak error process 	*/
/*  IN:	pendingGfx				*/
/*  RET:None					*/
/*----------------------------------------------*/
void
pakrwerr(int pendingGfx)
{
    /* Provide the display process if there is only one task which is processing or waiting for the process.*/
    if(pendingGfx < 2){
	makepakrwerrDL();

	/*Create trigger data*/
	nuContDataGetExAll(&ContTriger[0]);
    
	/*The game-over progressing process*/
	updateRwPakErr();
    }
}


/*--------------------------------------*/
/*   Record pad data to the buffer	*/
/*  IN:	None				*/
/*  RET:None				*/
/*--------------------------------------*/
void
SavePadData( void )
{
    int idx;
    
    for(idx = 0; idx < 4; idx++){
	ConTrace[TraceIdx][idx].button = nuContData[idx].button;
	ConTrace[TraceIdx][idx].stick_x = nuContData[idx].stick_x;
	ConTrace[TraceIdx][idx].stick_y = nuContData[idx].stick_y;
    }
    TraceIdx++;
}


/*------------------------------------------------------*/
/*  Record pad data to the Controller Pak    	*/
/*  IN:	None						*/
/*  RET:None						*/
/*------------------------------------------------------*/
void
SaveContPak( void )
{
    int idx1, idx2, idx3;
    u8 _al, _ah, stx, sty;
    /*100*60*/
    for(idx1 = 0; idx1 < 4; idx1++){
	for(idx2 = 0, idx3 = 0; idx3 < 100*60; idx2+=4, idx3++){
	    /* To each work once with the u8 size from the buffer*/
	    _al = (u8)ConTrace[idx3][idx1].button;
	    _ah = (u8)(ConTrace[idx3][idx1].button >> 8);
	    stx = (u8)ConTrace[idx3][idx1].stick_x;
	    sty = (u8)ConTrace[idx3][idx1].stick_y;

	    SaveBuf[idx2] = _al;
	    SaveBuf[idx2 + 1] = _ah;
	    SaveBuf[idx2 + 2] = stx;
	    SaveBuf[idx2 + 3] = sty;
	}
	/* Save to the pack, practically */
	nuContPakFileWrite(&pakFile[idx1], 0, 100*60*4,SaveBuf);
	ContPakOpen[idx1] = pakFile[idx1].error;
    }
}

/*------------------------------------------------------*/
/*  Read pad data from the Controller Pak 	*/
/*  IN:	None						*/
/*  RET:None						*/
/*------------------------------------------------------*/
void
LoadContPak( void )
{
    int idx1, idx2, idx3;
    u8 _al, _ah, stx, sty;

    /* Register the company code and game code */
    nuContPakCodeSet("64", "snake");
    
    for(idx1 = 0; idx1 < 4; idx1++){
	/* Open the note*/
	nuContPakFileOpenJis(&pakFile[idx1], "snake tail hack", ""
			     ,NU_CONT_PAK_MODE_NOCREATE, GAME_NOTE_SIZE + 31);
	/* Load from the pack, practically  */
	nuContPakFileRead(&pakFile[idx1], 0, 100*60*4,SaveBuf);
	ContPakOpen[idx1] = pakFile[idx1].error;

	if(pakFile[idx1].error == 0){
	    for(idx2 = 0, idx3 = 0; idx3 < 100*60; idx2+=4, idx3++){
		/* To each work once with the u8 size from the buffer  */
		_al = SaveBuf[idx2];
		_ah = SaveBuf[idx2 + 1];
		stx = SaveBuf[idx2 + 2];
		sty = SaveBuf[idx2 + 3];
	    
		ConTrace[idx3][idx1].button = (_ah << 8) + _al;
		ConTrace[idx3][idx1].stick_x = stx;
		ConTrace[idx3][idx1].stick_y = sty;
	    }
	}
    }
}


/*--------------------------------------*/
/*  Play back saved pad data 	*/
/*  IN:	None				*/
/*  RET:None				*/
/*--------------------------------------*/
void
ReplayPadData( void )
{
    int idx;
    
    for(idx = 0; idx < 4; idx++){
	nuContData[idx].button = ConTrace[TraceIdx][idx].button;
	nuContData[idx].stick_x = ConTrace[TraceIdx][idx].stick_x;
	nuContData[idx].stick_y = ConTrace[TraceIdx][idx].stick_y;
    }
    TraceIdx++;
}


/*--------------------------------------*/
/*  Open the Controller Pak	*/
/*  IN:	None				*/
/*  RET:None				*/
/*--------------------------------------*/
void
ContPakOpenAll( void )
{
    int idx;
    for(idx = 0; idx < 4; idx++){
	/*  Recognize the controller pack  */
	nuContPakOpen(&pakFile[idx], idx);
	if(pakFile[idx].type == NU_CONT_PAK_TYPE_RUMBLE){
	    ContPakOpen[idx] = RUMBLE_PAK;
	}else{
	    ContPakOpen[idx] = pakFile[idx].error;
	}
	/*PFS_ERR_NOPACK, PFS_ERR_NEW_PACK, PFS_ERR_CONTRFAIL*/
	/*The error process about PFS_ERR_ID_FATAL and PFS_ERR_DEVICE*/
#if 0
	switch(ContPakOpen[idx]){
	  case 0:
	    osSyncPrintf("PACK%d\tOK\n",idx);
	    break;
	  case PFS_ERR_NOPACK:
	    osSyncPrintf("PACK%d\tPFS_ERR_NOPACK\n",idx);
	    break;
	  case PFS_ERR_NEW_PACK:
	    osSyncPrintf("PACK%d\tPFS_ERR_NEW_PACK\n",idx);
	    break;
	  case PFS_ERR_CONTRFAIL:
	    osSyncPrintf("PACK%d\tPFS_ERR_CONTRFAIL\n",idx);
	    break;
	  case PFS_ERR_ID_FATAL:
	    osSyncPrintf("PACK%d\tPFS_ERR_ID_FATAL\n",idx);
	    break;
	  case PFS_ERR_DEVICE:
	    osSyncPrintf("PACK%d\tPFS_ERR_DEVICE\n",idx);
	    break;
	  case RUMBLE_PAK:
	    osSyncPrintf("PACK%d\tRUMBLE_PACK DETECTED\n",idx);
	    break;
	  default:
	    break;
	}
#endif
    }
}


/*--------------------------------------------------------------*/
/*  Check the status of the opened Controller Pak   	*/
/*  IN:	None							*/
/*  RET:None							*/
/*--------------------------------------------------------------*/
int
ChkPakStat( void )
{
    int idx;
    for(idx = 0; idx < 4; idx++){
	if(ContPakOpen[idx] != 0){
	    return(idx);
	}
    }
    return(-1);
}


/*--------------------------------------*/
/*   Open the game note of the opened   */
/*  Controller Pak     			*/
/*  IN:	None				*/
/*  RET:None				*/
/*--------------------------------------*/
void
NoteOpenAll( void )
{
    int idx;
    /* Register the company and game codes */
    nuContPakCodeSet("64", "snake");
    for(idx = 0; idx < 4; idx++){
	/* Open the note */
	nuContPakFileOpenJis(&pakFile[idx], "snake tail hack", ""
			     ,NU_CONT_PAK_MODE_CREATE, GAME_NOTE_SIZE + 31);

	if(pakFile[idx].error == PFS_DATA_FULL || pakFile[idx].error == PFS_DIR_FULL){
	    pakFile[idx].error = NOT_CREAT;
	    ContPakOpen[idx] = pakFile[idx].error;
	}else{
	    ContPakOpen[idx] = 0;
	}
    }
}