//---------------------------------------------------------------------------
// Movie Shit for Lethal Enforcer I & II
//---------------------------------------------------------------------------

#include <sys\types.h>
#include <libetc.h>
#include <libgte.h>
#include <libgpu.h>
#include <rand.h>
#include <libcd.h>
#include <memory.h>
#include <libpress.h>
#include "..\include\compile.h"
#include "..\include\readfile.h"


typedef struct
{
   int Score;
   int BulletType;
   int Bullets;
   int Lifepnt;
   int Counter;
   int GunX;
   int GunY;
   int ShotDrawn;
   int ShotX;
   int ShotY;
   int Grade;
   int Shots;
   int Hits;
   int VictimsMiss;
   int PlayerNbr;
} PLAYER;

typedef struct
{
   int Credits;
   PLAYER Plr1;
   PLAYER Plr2;
} Foreground;

void Init_Player (PLAYER *plr);
void Control_Read (int *trigger, Foreground *frgnd, int vabnbr, int *buttons);
extern int MusicVol;

#define TARGET SONY_PSX

#include <audio_xa.h>
#include "..\include\db.h"

#define POS_X        0
#define POS_Y        0
#define SCR_WIDTH    320
#define SCR_HEIGHT   240

#define PPW 1                           // pixel/short word (16 bits/pixel)
#define DCT_MODE 2                      // 16 bit decode

int StartFrame;
int EndFrame;

//-------------------------------------
// Decode environment
//-------------------------------------
typedef struct
{
   u_long   *vlcbuf[2];                 // VLC buffer
   int      vlcid;                      // Current decode buffer id
   u_short  *imgbuf[2];                 // Decode image buffer
   int      imgid;                      // Currently used buffer id
   RECT     rect[2];                    // Double buf origin(up left pnt) addr
   int      rectid;                     // Currently translating buffer id
   RECT     slice;                      // The region decoded by 1 DecDCTout()
   int      isdone;                     // The flag of decoding whole frame
   int      onmem;                      // Flag for memory streaming
} DECENV;

DECENV  dec;

//-------------------------------------
// Ring buffer for STREAMING
// Minimum size is two frame
//-------------------------------------
#define  RING_SIZE  256                 // 32 sectors
u_long   *Ring_Buff;

//-------------------------------------
// VLC buffer (double buffer)
// Stock the result of VLC decode
//-------------------------------------
#define  VLC_BUFF_SIZE  320 / 2 * 256   // Not correct value
u_long *vlcbuf0;
u_long *vlcbuf1;

//-------------------------------------
// Image buffer (double buffer)
// Stock the result of MDEC
// Rectangle of 16(width) by XX(height)
//-------------------------------------
#define  SLICE_IMG_SIZE  16 * PPW * SCR_HEIGHT
u_short *imgbuf0;
u_short *imgbuf1;

int  StrWidth  = 0;            // Resolution of movie
int  StrHeight = 0;
int  Rewind_Switch;            // The end flag set after last frame

//-------------------------------------
// Prototypes
//-------------------------------------
void   strSetDefDecEnv (DECENV *dec, int x0, int y0, int x1, int y1);
void   strInit (CdlLOC *loc, void (*callback)(...));
void   strCallback (...);
void   strNextVlc (DECENV *dec);
void   strSync (DECENV *dec, int mode);
u_long *strNext (DECENV *dec);
void   strKickCD (CdlLOC *loc);
void   repeatCallback (...);
u_long *strNext ();

//-------------------------------------
// Play a movie in a foreground process
//-------------------------------------
int PlayMovie (char *filename, int start, int end, u_long memoryp,
                      DoubleBuffer *db, int passit)
{
   DISPENV     disp;       // Display buffer
   DRAWENV     draw;       // Drawing buffer
   int         id;         // Display buffer id
   CdlFILE     file;
   int         padd;
   int         trigger = 0, buttons = 0;
   Foreground  frgnd = {4,0,0};

   #ifdef TARGETCD
      #ifdef PLAY_CDXAS
         u_char param[4], result[16];
         AudioChangeMasterVolume (127);
         CdControlB (CdlDemute, NULL, NULL);
      #endif
   #endif

   Init_Player (&frgnd.Plr1);
   frgnd.Plr1.PlayerNbr = 0;
   Init_Player (&frgnd.Plr2);
   frgnd.Plr2.PlayerNbr = 1;

   Ring_Buff = (u_long *)memoryp;
   memoryp += RING_SIZE * SECTOR_SIZE * 4;
   vlcbuf0 = (u_long *)memoryp;
   memoryp += VLC_BUFF_SIZE * 4;
   vlcbuf1 = (u_long *)memoryp;
   memoryp += VLC_BUFF_SIZE * 4;
   imgbuf0 = (u_short *)memoryp;
   memoryp += SLICE_IMG_SIZE * 2;
   imgbuf1 = (u_short *)memoryp;
   memoryp += SLICE_IMG_SIZE * 2;

   StartFrame = start;
   EndFrame = end;
    
   // Search file
   if (CdSearchFile (&file, filename) == 0)
   {
      ResetGraph (3);
      StopCallback ();
      return 0;
   }
    
   // Set the position of vram
   strSetDefDecEnv (&dec, POS_X, POS_Y, POS_X, POS_Y + SCR_HEIGHT);
    
   // Init streaming system & kick cd
   strInit (&file.pos, strCallback);
    
   // VLC decode the first frame
   strNextVlc (&dec);
    
   Rewind_Switch = 0;
    
   while (1)
   {
      // Start DCT decoding the result of VLC decoded data
      DecDCTin (dec.vlcbuf[dec.vlcid], DCT_MODE);
        
      // Prepare for recieving the result of DCT decode
      // Next DecDCTout is called in DecDCToutCallback
      DecDCTout ((u_long *)dec.imgbuf[dec.imgid], dec.slice.w * dec.slice.h / 2);
        
      // Decode the next frame's VLC data
      strNextVlc (&dec);
        
      // Wait for whole decode process per 1 frame
      strSync (&dec, 0);
        
      // Wait for V-Vlank
      VSync (0);
        
      // Swap the display buffer
      // Notice that the disp. buffer is the opposite side of decoding buffer
      id = dec.rectid ? 0 : 1;
      SetDefDispEnv (&disp, 0, (id) * 240, SCR_WIDTH * PPW, SCR_HEIGHT);
        
      PutDispEnv (&disp);
      SetDispMask (1);              // Display enable
        
      if (Rewind_Switch == 1)
         break;
        
      if (passit)
         Control_Read (&trigger, &frgnd, -1, &buttons);

      if (buttons & 2054)             // Check "START" buttons & "X" buttons
      {
         DecDCToutCallback (0);
         StUnSetRing ();
         CdControlB (CdlPause, 0, 0);

         db->ClearDrawArea (0,0,0);
         db->Swap (0);
         db->Swap (0);

         return (0);
      }
   }
    
   // Post processing of animation routine
   DecDCToutCallback (0);
   StUnSetRing ();
   CdControlB (CdlPause, 0, 0);

   db->ClearDrawArea (0,0,0);
   db->Swap (0);
   db->Swap (0);

   if (Rewind_Switch == 0)
   {
      ResetGraph (3);
      StopCallback ();
      return (0);
   }
   else
      return (1);
}

//-------------------------------------
// Init DECENV
// buffer0 (x0,y0), buffer1 (x1,y1)
//-------------------------------------
void strSetDefDecEnv (DECENV *dec, int x0, int y0, int x1, int y1)
{
   dec->vlcbuf[0] = vlcbuf0;
   dec->vlcbuf[1] = vlcbuf1;
   dec->vlcid     = 0;

   dec->imgbuf[0] = imgbuf0;
   dec->imgbuf[1] = imgbuf1;
   dec->imgid     = 0;

   // Width and height of rect[] are set dynamicaly according to STR data
   dec->rect[0].x = x0;
   dec->rect[0].y = y0;
   dec->rect[1].x = x1;
   dec->rect[1].y = y1;
   dec->rectid    = 0;

   dec->slice.x = x0;
   dec->slice.y = y0;
   dec->slice.w = 16 * PPW;

   dec->isdone = 0;
   dec->onmem  = 0;
}



//-------------------------------------
// Init the streaming environment and
// start the cdrom
//-------------------------------------
void strInit (CdlLOC *loc, void (*callback)(...))
{
   // Cold reset mdec
   DecDCTReset (0);
  
   // Set the callback after 1 block MDEC decoding
   DecDCToutCallback (callback);
    
   // Set the ring buffer
   StSetRing (Ring_Buff, RING_SIZE);

   // Init the streaming library
   // end frame is set endless
   StSetStream (0, StartFrame, 0xffffffff, 0, repeatCallback);
    
   // Start the cdrom
   strKickCD (loc);
}

//-------------------------------------
// back ground process
// Callback of DecDCTout ()
//-------------------------------------
void strCallback (...)
{
   RECT snap_rect;
   int  id;
  
   id = dec.imgid;
   snap_rect = dec.slice;
  
   // Switch the id of decoding buffer
   dec.imgid = dec.imgid ? 0 : 1;

   // Update slice (rectangle) position
   dec.slice.x += dec.slice.w;
    
   // Remaining slice ?
   if (dec.slice.x < dec.rect[dec.rectid].x + dec.rect[dec.rectid].w)
   {
      // Prepare for next slice
      DecDCTout ((u_long *)dec.imgbuf[dec.imgid],
                 dec.slice.w * dec.slice.h / 2);
   }
   else             // Last slice; end of 1 frame
   {
      // Set the decoding done flag
      dec.isdone = 1;
        
      // Update the position on VRAM
      dec.rectid = dec.rectid ? 0 : 1;
      dec.slice.x = dec.rect[dec.rectid].x;
      dec.slice.y = dec.rect[dec.rectid].y;
   }
  
   // Transfer the decoded data to VRAM
   LoadImage (&snap_rect, (u_long *)dec.imgbuf[id]);
}

//-------------------------------------
//
//-------------------------------------
void repeatCallback (...)
{
   dec.onmem = 1;
}

//-------------------------------------
// Execute VLC decoding.  The decoding
// data is the next frame's
//-------------------------------------
void strNextVlc (DECENV *dec)
{
   int            cnt = WAIT_TIME;
   u_long         *next;

   // Get the 1 frame streaming data
   while ((next = strNext(dec)) == 0)
   {
      if (--cnt == 0)
         return;
   }
    
   // Switch the decoding area
   dec->vlcid = dec->vlcid ? 0 : 1;

   // VLC decode
   DecDCTvlc (next, dec->vlcbuf[dec->vlcid]);

   // Free the ring buffer
   StFreeRing (next);

   return;
}

//-------------------------------------
// Get the 1 frame streaming data
// return value normal end:
//    top address of 1 frame streaming
//    data
// error:
//    NULL :
//-------------------------------------
u_long *strNext (DECENV *dec)
{
   u_long      *addr;
   StHEADER    *sector;
   int         cnt = WAIT_TIME;
    
   // Get the 1 frame streaming data withe TIME-OUT
   if (dec->onmem)
   {
      while (StGetNextS ((u_long **)&addr, (u_long **)&sector))
      {
         if (--cnt == 0)
            return (0);
      }
   }
   else
   {
      while (StGetNext ((u_long **)&addr, (u_long **)&sector))
      {
         if (--cnt == 0)
            return(0);
      }
   }

   // If the frame number greater than the end frame, set the end switch
   if (sector->frameCount >= EndFrame)
      Rewind_Switch = 1;
    
   // If the resolution differ to previous frame, clear frame buffer
   if (StrWidth != sector->width || StrHeight != sector->height)
   {
      RECT  rect;
      setRECT (&rect, 0, 0, SCR_WIDTH * PPW, SCR_HEIGHT * 2);
      ClearImage (&rect, 0, 0, 0);
        
      StrWidth  = sector->width;
      StrHeight = sector->height;
   }
    
   // Set DECENV according to the data on the STR format
   dec->rect[0].w = dec->rect[1].w = StrWidth * PPW;
   dec->rect[0].h = dec->rect[1].h = StrHeight;
   dec->slice.h   = StrHeight;
    
   return(addr);
}

//-------------------------------------
// Wait for finish decodeing 1 frame
// with TIME-OUT
//-------------------------------------
void strSync (DECENV *dec, int mode)
{
   volatile u_long cnt = WAIT_TIME;

   // Wait for the decod is done flag set by background process
   while (dec->isdone == 0)
   {
      if (--cnt == 0)
      {
         // If timeout force to switch buffer
         dec->isdone = 1;
         dec->rectid = dec->rectid ? 0 : 1;
         dec->slice.x = dec->rect[dec->rectid].x;
         dec->slice.y = dec->rect[dec->rectid].y;
      }
   }

   dec->isdone = 0;
}

//-------------------------------------
// Start streaming
//-------------------------------------
void strKickCD (CdlLOC *loc)
{
   loop:
   // Seek to the destination
   while (CdControl (CdlSetloc, (u_char *)loc, 0) == 0);
    
   // out the read command with streaming mode
   if (CdRead2 (CdlModeStream | CdlModeSpeed | CdlModeRT) == 0)
      goto loop;
}
