    /*============================================================================
      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;
    	}
        }
    }