#include "generic.h"
#include "lfptypes.h"
#include "define.h"
#include "ml.h"

#include "skel.h"
#include "playgfx.h"
#include "crowd.h"

#include "audio.h"
#include "hotspot.h"

#ifdef PC
 #include "textcode.h"
 #include "kernel.h"
#endif
#include "camera.h"
#include "cammodes.h"
#include "ctrl.h"
#include "min.h"


#include "gamefx.h"

extern u8
	*GameBank;


#ifdef N64
 #ifdef DEBUG
  #define REP_BUF_SZ      300
  #define REP_BUF_WID     100
 #else
  #define REP_BUF_SZ      300
  #define REP_BUF_WID     70
 #endif

#else
 #define REP_BUF_SZ      600
 #define REP_BUF_WID     300
#endif


	extern int	p4f,p9f,pbf;


extern s8
	RunAttract;


#ifdef N64

	extern s32
		Banners_Scroll,
		Banners_Which;

#endif

#define	TA_B	11


static int
	SfxId = 0,
	SfxVol = 0,
	SfxFrame;

static SI   dummy;
static SW   top_frame;
global SW   last_saved_frame;                   /*which frame of replay buffer was last used by game*/
static SI   replay_frame;						/*which frame of replay buffer shown by replay*/
static bool replay_wrap_flag;                   /*is replay buffer wrapped round end?*/
global SI   *replay_buf=NULL;

extern char *main_names[];
extern char *top_names[];
extern char *leg_names[];

static int save_space[500];						//buffer to copy all variables overwritten by replay code to
static bool not_switched_yet;					//
static bool forward_flag;

static int
	HotSpotBuf,
	CloudsBuf;

static int  replay_ctr;

global bool replay_flag;                        //is replay on/off?
global int replay_pad;							//who pressed start
global SW replay_player=0;						//which player am I looking at

char idString[80];	// String holding player id, i.e.  "Player #1"
char mrString[80];	// String holding main routine name, i.e. "Finger_Roll1"
char tpString[80];	// String holding top routine name, i.e. "Dribble Animate"
char lgString[80];	// String holding leg routine name, i.e. "Running"

static int pf;
static int pf_lock;
static int cf[4];
static int rbh;

typedef struct rep_lists
{
	int *addr;
	UB	r_shift;
	UB	size;
}rep_listt;


//replay list (address of variable, shift right val, no of bits to be saved)

static rep_listt replay_list[]={
&player[0].xco,12,13,
&player[0].yco,12,14,
&player[0].zco,15,9,
&player[0].angle,8,8,
&player[0].pitch,2,3,

&player[0].last_top_anim,0,TA_B,
&player[0].last_top_frame,7,9,
&player[0].top_anim,0,TA_B,
&player[0].top_frame,7,9,
&player[0].top_blend_ctr,4,5,

&player[0].last_leg_anim,0,9,
&player[0].last_leg_frame,7,9,
&player[0].leg_anim,0,9,
&player[0].leg_frame,7,9,
&player[0].leg_blend_ctr,4,5,
&player[0].ikBlend,6,4,


&player[1].xco,12,13,
&player[1].yco,12,14,
&player[1].zco,15,9,
&player[1].angle,8,8,
&player[1].pitch,2,3,

&player[1].last_top_anim,0,TA_B,
&player[1].last_top_frame,7,9,
&player[1].top_anim,0,TA_B,
&player[1].top_frame,7,9,
&player[1].top_blend_ctr,4,5,

&player[1].last_leg_anim,0,9,
&player[1].last_leg_frame,7,9,
&player[1].leg_anim,0,9,
&player[1].leg_frame,7,9,
&player[1].leg_blend_ctr,4,5,
&player[1].ikBlend,6,4,


&player[2].xco,12,13,
&player[2].yco,12,14,
&player[2].zco,15,9,
&player[2].angle,8,8,
&player[2].pitch,2,3,

&player[2].last_top_anim,0,TA_B,
&player[2].last_top_frame,7,9,
&player[2].top_anim,0,TA_B,
&player[2].top_frame,7,9,
&player[2].top_blend_ctr,4,5,

&player[2].last_leg_anim,0,9,
&player[2].last_leg_frame,7,9,
&player[2].leg_anim,0,9,
&player[2].leg_frame,7,9,
&player[2].leg_blend_ctr,4,5,
&player[2].ikBlend,6,4,


&player[3].xco,12,13,
&player[3].yco,12,14,
&player[3].zco,15,9,
&player[3].angle,8,8,
&player[3].pitch,2,3,

&player[3].last_top_anim,0,TA_B,
&player[3].last_top_frame,7,9,
&player[3].top_anim,0,TA_B,
&player[3].top_frame,7,9,
&player[3].top_blend_ctr,4,5,

&player[3].last_leg_anim,0,9,
&player[3].last_leg_frame,7,9,
&player[3].leg_anim,0,9,
&player[3].leg_frame,7,9,
&player[3].leg_blend_ctr,4,5,
&player[3].ikBlend,6,4,


&player[4].xco,12,13,
&player[4].yco,12,14,
&player[4].zco,15,9,
&player[4].angle,8,8,
&player[4].pitch,2,3,

&player[4].last_top_anim,0,TA_B,
&player[4].last_top_frame,7,9,
&player[4].top_anim,0,TA_B,
&player[4].top_frame,7,9,
&player[4].top_blend_ctr,4,5,

&player[4].last_leg_anim,0,9,
&player[4].last_leg_frame,7,9,
&player[4].leg_anim,0,9,
&player[4].leg_frame,7,9,
&player[4].leg_blend_ctr,4,5,
&player[4].ikBlend,6,4,


&player[5].xco,12,13,
&player[5].yco,12,14,
&player[5].zco,15,9,
&player[5].angle,8,8,
&player[5].pitch,2,3,

&player[5].last_top_anim,0,TA_B,
&player[5].last_top_frame,7,9,
&player[5].top_anim,0,TA_B,
&player[5].top_frame,7,9,
&player[5].top_blend_ctr,4,5,

&player[5].last_leg_anim,0,9,
&player[5].last_leg_frame,7,9,
&player[5].leg_anim,0,9,
&player[5].leg_frame,7,9,
&player[5].leg_blend_ctr,4,5,
&player[5].ikBlend,6,4,


&player[6].xco,12,13,
&player[6].yco,12,14,
&player[6].zco,15,9,
&player[6].angle,8,8,
&player[6].pitch,2,3,

&player[6].last_top_anim,0,TA_B,
&player[6].last_top_frame,7,9,
&player[6].top_anim,0,TA_B,
&player[6].top_frame,7,9,
&player[6].top_blend_ctr,4,5,

&player[6].last_leg_anim,0,9,
&player[6].last_leg_frame,7,9,
&player[6].leg_anim,0,9,
&player[6].leg_frame,7,9,
&player[6].leg_blend_ctr,4,5,
&player[6].ikBlend,6,4,


&player[7].xco,12,13,
&player[7].yco,12,14,
&player[7].zco,15,9,
&player[7].angle,8,8,
&player[7].pitch,2,3,

&player[7].last_top_anim,0,TA_B,
&player[7].last_top_frame,7,9,
&player[7].top_anim,0,TA_B,
&player[7].top_frame,7,9,
&player[7].top_blend_ctr,4,5,

&player[7].last_leg_anim,0,9,
&player[7].last_leg_frame,7,9,
&player[7].leg_anim,0,9,
&player[7].leg_frame,7,9,
&player[7].leg_blend_ctr,4,5,
&player[7].ikBlend,6,4,


&player[8].xco,12,13,
&player[8].yco,12,14,
&player[8].zco,15,9,
&player[8].angle,8,8,
&player[8].pitch,2,3,

&player[8].last_top_anim,0,TA_B,
&player[8].last_top_frame,7,9,
&player[8].top_anim,0,TA_B,
&player[8].top_frame,7,9,
&player[8].top_blend_ctr,4,5,

&player[8].last_leg_anim,0,9,
&player[8].last_leg_frame,7,9,
&player[8].leg_anim,0,9,
&player[8].leg_frame,7,9,
&player[8].leg_blend_ctr,4,5,
&player[8].ikBlend,6,4,


&player[9].xco,12,13,
&player[9].yco,12,14,
&player[9].zco,15,9,
&player[9].angle,8,8,
&player[9].pitch,2,3,

&player[9].last_top_anim,0,TA_B,
&player[9].last_top_frame,7,9,
&player[9].top_anim,0,TA_B,
&player[9].top_frame,7,9,
&player[9].top_blend_ctr,4,5,

&player[9].last_leg_anim,0,9,
&player[9].last_leg_frame,7,9,
&player[9].leg_anim,0,9,
&player[9].leg_frame,7,9,
&player[9].leg_blend_ctr,4,5,
&player[9].ikBlend,6,4,


&player[10].xco,12,13,
&player[10].yco,12,14,
&player[10].zco,15,14,
&last_bx,12,13,
&last_by,12,14,
&last_bz,12,13,

&scored_flag,0,2,
&last_dunker,0,5,

&netanimno[0],0,5,
&netframe[0],0,16,
&nethdg[0],0,16,

&netanimno[1],0,5,
&netframe[1],0,16,
&nethdg[1],0,16,

&player[0].flags,0,8,
&player[1].flags,0,8,
&player[2].flags,0,8,
&player[3].flags,0,8,
&player[4].flags,0,8,
&player[5].flags,0,8,
&player[6].flags,0,8,
&player[7].flags,0,8,
&player[8].flags,0,8,
&player[9].flags,0,8,

&glassScroll,0,6,
&swingHeight,2,8,

&controls[0],0,5,
&controls[1],0,5,
&controls[2],0,5,
&controls[3],0,5,

&ball_rotx, 10, 6,
&ball_roty, 10, 6,
&ball_rotz, 10, 6,

&ball_handler,0,5,
&int_recvr,0,5,
//(int *)&bhp,2,30,
&possession_flag,0,2,
&current_basket_y,20,5,

&time_left,0,32,
&shot_clock,2,10,

#ifdef N64
	(int *)&Banners_Scroll,0,12,
	(int *)&Banners_Which,0,4,
#endif




#ifdef DEBUG
 &player[1].target_x, 12, 13,
 &player[1].target_y, 12, 14,
 &player[6].target_x, 12, 13,
 &player[6].target_y, 12, 14,
	
 &lp_ctr, 0, 32,
 &status_var1, 0, 32,
 &status_var2, 0, 32,
 &status_var3, 0, 32,
 &status_var4, 0, 32,
 &status_var5, 0, 32,
 &status_var6, 0, 32,
 &status_var7, 0, 32,

 &crossX, 12, 14,
 &crossY, 12, 14,


//&player[4].mr_no ,0,12,
//&player[4].top_no,0,12,
//&player[4].leg_no,0,12,

#ifdef PC
//&ik_blend_val, 6,4,

&player[0].d_angle,0,16,
&player[0].force,4,6,

&player[1].d_angle,0,16,
&player[1].force,4,6,

&player[2].d_angle,0,16,
&player[2].force,4,6,

&player[3].d_angle,0,16,
&player[3].force,4,6,

&player[4].d_angle,0,16,
&player[4].force,4,6,

&player[5].d_angle,0,16,
&player[5].force,4,6,

&player[6].d_angle,0,16,
&player[6].force,4,6,

&player[7].d_angle,0,16,
&player[7].force,4,6,

&player[8].d_angle,0,16,
&player[8].force,4,6,

&player[9].d_angle,0,16,
&player[9].force,4,6,

&player[0].score, 0, 32,
&player[1].score, 0, 32,
&player[2].score, 0, 32,
&player[3].score, 0, 32,
&player[4].score, 0, 32,
&player[5].score, 0, 32,
&player[6].score, 0, 32,
&player[7].score, 0, 32,
&player[8].score, 0, 32,
&player[9].score, 0, 32,


&threat_plyr, 0, 5,


&dunk_flag, 0, 1,

 #endif
#endif


#ifdef DEBUG

&player[0].mr_no ,0,12,
&player[0].top_no,0,8,
&player[0].leg_no,0,8,

&player[1].mr_no ,0,12,
&player[1].top_no,0,8,
&player[1].leg_no,0,8,

&player[2].mr_no ,0,12,
&player[2].top_no,0,8,
&player[2].leg_no,0,8,

&player[3].mr_no ,0,12,
&player[3].top_no,0,8,
&player[3].leg_no,0,8,

&player[4].mr_no ,0,12,
&player[4].top_no,0,8,
&player[4].leg_no,0,8,

&player[5].mr_no ,0,12,
&player[5].top_no,0,8,
&player[5].leg_no,0,8,

&player[6].mr_no ,0,12,
&player[6].top_no,0,8,
&player[6].leg_no,0,8,

&player[7].mr_no ,0,12,
&player[7].top_no,0,8,
&player[7].leg_no,0,8,

&player[8].mr_no ,0,12,
&player[8].top_no,0,8,
&player[8].leg_no,0,8,

&player[9].mr_no ,0,12,
&player[9].top_no,0,8,
&player[9].leg_no,0,8,

&dunk_flag,0,1,
&pass_flag,0,1,

// Data for determining AI's chosen position
&player[0].target_x,12,13,
&player[0].target_y,12,14,
&player[1].target_x,12,13,
&player[1].target_y,12,14,
&player[2].target_x,12,13,
&player[2].target_y,12,14,
&player[3].target_x,12,13,
&player[3].target_y,12,14,
&player[4].target_x,12,13,
&player[4].target_y,12,14,
&player[5].target_x,12,13,
&player[5].target_y,12,14,
&player[6].target_x,12,13,
&player[6].target_y,12,14,
&player[7].target_x,12,13,
&player[7].target_y,12,14,
&player[8].target_x,12,13,
&player[8].target_y,12,14,
&player[9].target_x,12,13,
&player[9].target_y,12,14,

&player[9].force,4,6,
&player[9].hand_check_flag,0,6,
&p9f,0,32,
&p4f,0,32,
&pbf,0,32,
&player[4].basket_angle,0,32,

#endif

&fast_break_flag,0,32,

(int *)&PlayerInfos[0].FaceAnim,8,13,
(int *)&PlayerInfos[1].FaceAnim,8,13,
(int *)&PlayerInfos[2].FaceAnim,8,13,
(int *)&PlayerInfos[3].FaceAnim,8,13,
(int *)&PlayerInfos[4].FaceAnim,8,13,
(int *)&PlayerInfos[5].FaceAnim,8,13,
(int *)&PlayerInfos[6].FaceAnim,8,13,
(int *)&PlayerInfos[7].FaceAnim,8,13,
(int *)&PlayerInfos[8].FaceAnim,8,13,
(int *)&PlayerInfos[9].FaceAnim,8,13,

(int *)&CrowdAnims[0],0,32,
(int *)&CrowdAnims[1],0,32,
(int *)&CrowdAnims[2],0,32,

&HotSpotBuf, 0, 32,
&CloudsBuf, 0, 2,

&SfxId, 0, 5,
&SfxVol, 0, 8,

(int *)&dummy,0,32,
NULL,0,0,
};


static char string[160];



static void ProcessSfx()
{
	SfxId &= 31;
	SfxVol &= 255;

	if (SfxId)
	{
		SfxId--;

		Audio_SfxPlay( GameBank, SfxId, (SfxVol * SfxVolume) >> 7 );

		if (SfxId == SFX_SBOOM && AttractMode)
			ShakeScreen = SHAKE_SIZE;
	}
}


static int possflag;

void copy_vars(void)
{
rep_listt *load_ptr;
int *save_spc_ptr;

	HotSpot_Save( (s32 *)&HotSpotBuf );
	Clouds_Save( (s32 *)&CloudsBuf );

	save_spc_ptr= save_space;
	load_ptr	= replay_list;

	do{
		*save_spc_ptr++ = *(load_ptr->addr);
		load_ptr++;
	}
	while (load_ptr->addr != &dummy);

	//possflag=possession_flag;
}



void copy_back_vars(void)
{
rep_listt *load_ptr;
int *save_spc_ptr;

	save_spc_ptr= save_space;
	load_ptr	= replay_list;

	do{
		*(load_ptr->addr)	= *save_spc_ptr++;
		load_ptr++;
	}
	while (load_ptr->addr != &dummy);

	HotSpot_Load( (s32 *)&HotSpotBuf );
	Clouds_Load( (s32 *)&CloudsBuf );

	bhp = &player[ball_handler];

	//possession_flag=possflag;
}




static int mask[33]={
0,1,3,7,0xf,
0x1f,0x3f,0x7f,0xff,
0x1ff,0x3ff,0x7ff,0xfff,
0x1fff,0x3fff,0x7fff,0xffff,
0x1ffff,0x3ffff,0x7ffff,0xfffff,
0x1fffff,0x3fffff,0x7fffff,0xffffff,
0x1ffffff,0x3ffffff,0x7ffffff,0xfffffff,
0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff,
};



void save_replay(void)
{
//int total = 0;

int	*save_ptr;
int	bits_left;
rep_listt *load_ptr;
int	load_reg,acc;
int	r_shift,size;

	HotSpot_Save( (s32 *)&HotSpotBuf );
	Clouds_Save( (s32 *)&CloudsBuf );

	dummy	= -1;

	last_saved_frame = top_frame;

	save_ptr	= &replay_buf[top_frame*REP_BUF_WID];

	acc			= 0;
	bits_left	= 32;
	load_ptr	= replay_list;

	while (load_ptr->addr)
	{
		load_reg= *(load_ptr->addr);

		r_shift	= load_ptr->r_shift;

		if (r_shift)	load_reg >>= r_shift;

		size	= load_ptr->size;

//total += size;

		load_reg &= mask[size];

		if (size <= bits_left)
		{
			if (size == bits_left)
			{
				acc			+=load_reg;
				*save_ptr++	= acc;
				acc 		= 0;
				bits_left	= 32;
			}
			else
			{
				acc	+= load_reg << (bits_left-size);
				bits_left -= size;
			}
		}
		else
		{
			acc			+=(unsigned int) load_reg >>(size - bits_left);
			*save_ptr++	= acc;
			bits_left	+= 32-size;
			acc			= load_reg<<bits_left;
		}

		load_ptr++;
	}

	if ((save_ptr-&replay_buf[top_frame*REP_BUF_WID]) > REP_BUF_WID)
	{
		char
			text[64],
			numstr[8];

		memset( numstr, 0, 8 );
		IntToDecStr( save_ptr - &replay_buf[top_frame*REP_BUF_WID], numstr );
		sprintf( text, "Replay buf needs to be %s ", numstr );
		FatalError( text, __FILE__, __LINE__ );
	}

	SfxId = 0;
	SfxVol = 0;

#ifdef PC

	if (top_frame==0)
	{
		printf("replay buffer is %d bytes/frame\n", (save_ptr-&replay_buf[top_frame*REP_BUF_WID]) * 4);
	}
#endif
}






void load_replay(int new_frame)
{
int	*saved_ptr;
int	bits_left;
rep_listt *load_ptr;
int	load_reg,acc;
int	r_shift,size;
int *end_addr;

int buffer[300];
int *buf_ptr;
playert *plyr;
int frac;
SW diff;

    end_addr	= &dummy;

	saved_ptr	= &replay_buf[(new_frame>>8)*REP_BUF_WID];
	load_ptr	= replay_list;
	acc			= *saved_ptr++;
	bits_left	= 32;

	do{
		size	= load_ptr->size;
		if (size <= bits_left)
		{
			load_reg	= acc >> (32-size);
			acc			<<= size;
			bits_left	-= size;
			if (!bits_left)
			{
				acc	= *saved_ptr++;
				bits_left =32;
			}
		}
		else
		{
            load_reg  =  acc >> (32-size);
            bits_left += (32-size);
            acc       =  *saved_ptr++;
            load_reg  += (UI) acc >> bits_left;
            acc       <<= 32-bits_left;
		}

		r_shift	= load_ptr->r_shift;
		if (r_shift)
		{
			load_reg <<= r_shift;
		}

		*(load_ptr->addr) = load_reg;
		load_ptr++;
	}
    while (load_ptr->addr != end_addr);

	if (new_frame & 0xff)
	{
//load next frame

		saved_ptr	= &replay_buf[(((new_frame>>8)+1) % REP_BUF_SZ)*REP_BUF_WID];
		load_ptr	= replay_list;
		acc			= *saved_ptr++;
		bits_left	= 32;
		buf_ptr		= buffer;

		do{
			size	= load_ptr->size;
			if (size <= bits_left)
			{
				load_reg	= acc >> (32-size);
				acc			<<= size;
				bits_left	-= size;
				if (!bits_left)
				{
					acc	= *saved_ptr++;
					bits_left =32;
				}
			}
			else
			{
				load_reg  =  acc >> (32-size);
				bits_left += (32-size);
				acc       =  *saved_ptr++;
				load_reg  += (UI) acc >> bits_left;
				acc       <<= 32-bits_left;
			}

			r_shift	= load_ptr->r_shift;
			if (r_shift)	load_reg <<= r_shift;

			*buf_ptr++ = load_reg;
			load_ptr++;
		}
		while (load_ptr->addr != end_addr);

		frac = new_frame & 0xff;
		buf_ptr	  = buffer;

		load_ptr  = replay_list;
		for (plyr = player; plyr< &ball; plyr++)
		{
			plyr->xco = ((plyr->xco>>8) * (0x100-frac))
					   +((*buf_ptr++ >> 8) * frac);

			plyr->yco = ((plyr->yco>>8) * (0x100-frac))
					   +((*buf_ptr++ >> 8) * frac);

			plyr->zco = ((plyr->zco>>8) * (0x100-frac))
					   +((*buf_ptr++ >> 8) * frac);

			if (plyr->zco<0)	plyr->zco=0;


			diff = ((SW)(*buf_ptr++) - (SW)plyr->angle);
			plyr->angle += (diff * frac) >> 8;

			plyr->pitch = (plyr->pitch * (0x100-frac)
						 + (*buf_ptr++ * frac) ) >> 8;
	/*
	&player[0].last_top_anim,0,7,
	&player[0].last_top_frame,7,9,
	&player[0].top_anim,0,7,
	&player[0].top_frame,7,9,
	&player[0].top_blend_ctr,4,6,
	  */

			if ((buf_ptr[4] != 0) && (plyr->top_blend_ctr == 0))
			{
				//started blend
				plyr->top_blend_ctr	 = ( (0x100*(0x100-frac)) + ((buf_ptr[4]&0xff) *frac) ) >> 8;

				plyr->last_top_anim  = buf_ptr[0];
				plyr->last_top_frame = buf_ptr[1];
				plyr->top_anim		 = buf_ptr[2];
				plyr->top_frame		 = buf_ptr[3];
			}
			else
			{
				if (plyr->top_anim == buf_ptr[2])
				{
					if (plyr->last_top_frame > buf_ptr[1] && playerAnims[abs(plyr->top_anim)].loopFlag)
						buf_ptr[1] += (playerAnims[abs(plyr->last_top_anim)].a->numFrames<<8);

					plyr->last_top_frame = ((plyr->last_top_frame * (0x100-frac) + (buf_ptr[1] * frac) ) >> 8)
											% (playerAnims[abs(plyr->last_top_anim)].a->numFrames<<8);

					if (plyr->top_frame > buf_ptr[3] && playerAnims[abs(plyr->top_anim)].loopFlag)
						buf_ptr[3] += (playerAnims[abs(plyr->top_anim)].a->numFrames<<8);

					plyr->top_frame		 = ((plyr->top_frame * (0x100-frac) + (buf_ptr[3] * frac) ) >> 8)
											% (playerAnims[abs(plyr->top_anim)].a->numFrames<<8);

					plyr->top_blend_ctr	 = ((plyr->top_blend_ctr&0xff)*(0x100-frac) + (buf_ptr[4]&0xff)*frac) >> 8;
				}
				else
				{
					plyr->last_top_anim = buf_ptr[0];
					plyr->last_top_frame = buf_ptr[1];
					plyr->top_anim		 = buf_ptr[2];
					plyr->top_frame		 = buf_ptr[3];
					plyr->top_blend_ctr  = 0x100-frac;
				}
			}

			buf_ptr += 5;

			if ((buf_ptr[4] != 0) & (plyr->leg_blend_ctr == 0))
			{
				//started blend
				plyr->leg_blend_ctr	 = ( (0x100*(0x100-frac)) + ((buf_ptr[4] & 0xff) *frac) ) >> 8;

				plyr->last_leg_anim  = buf_ptr[0];
				plyr->last_leg_frame = buf_ptr[1];
				plyr->leg_anim		 = buf_ptr[2];
				plyr->leg_frame		 = buf_ptr[3];
			}
			else
			{
				if (plyr->leg_anim == buf_ptr[2])
				{
					if (plyr->last_leg_frame > buf_ptr[1])		buf_ptr[1] += (legAnims[abs(plyr->last_leg_anim)].a->numFrames<<8);

					plyr->last_leg_frame = ((plyr->last_leg_frame * (0x100-frac) + (buf_ptr[1] * frac) ) >> 8)
											% (legAnims[abs(plyr->last_leg_anim)].a->numFrames<<8);

					if (plyr->leg_frame > buf_ptr[3])			buf_ptr[3] += (legAnims[abs(plyr->leg_anim)].a->numFrames<<8);

					plyr->leg_frame		 = ((plyr->leg_frame * (0x100-frac) + (buf_ptr[3] * frac) ) >> 8)
											% (legAnims[abs(plyr->leg_anim)].a->numFrames<<8);


					plyr->leg_blend_ctr	 = ((plyr->leg_blend_ctr&0xff)*(0x100-frac) + (buf_ptr[4]&0xff)*frac) >> 8;
				}
				else
				{
					plyr->last_leg_anim  = buf_ptr[0];
					plyr->last_leg_frame = buf_ptr[1];
					plyr->leg_anim		 = buf_ptr[2];
					plyr->leg_frame		 = buf_ptr[3];
					plyr->leg_blend_ctr  = 0x100-frac;
				}
			}

			buf_ptr += 5;

			plyr->ikBlend = (plyr->ikBlend * (0x100-frac)
							+ (*buf_ptr++ * frac) ) >> 8;
		}


		if (abs(*buf_ptr - plyr->xco) > 0x100000)	plyr->xco = *buf_ptr;
		plyr->xco = ((plyr->xco>>8) * (0x100-frac))
				   +((*buf_ptr++ >> 8) * frac);

		if (abs(*buf_ptr - plyr->yco) > 0x100000)	plyr->yco = *buf_ptr;
		plyr->yco = ((plyr->yco>>8) * (0x100-frac))
				   +((*buf_ptr++ >> 8) * frac);

		if (abs(*buf_ptr - plyr->zco) > 0x100000)	plyr->zco = *buf_ptr;
		plyr->zco = ((plyr->zco>>8) * (0x100-frac))
				   +((*buf_ptr++ >> 8) * frac);

		if (plyr->zco<0)	plyr->zco=0;

		last_bx = ((last_bx>>8) * (0x100-frac))
				   +((*buf_ptr++ >> 8) * frac);

		last_by = ((last_by>>8) * (0x100-frac))
				   +((*buf_ptr++ >> 8) * frac);

		last_bz = ((last_bz>>8) * (0x100-frac))
				   +((*buf_ptr++ >> 8) * frac);

		if (last_bz<0)	last_bz=0;

		buf_ptr++;	// scored_flag
		buf_ptr++;	// last_dunker

		//blend net frames if same animation is used
		if (netanimno[0] == buf_ptr[0])
		{
			netframe[0] = ((netframe[0] * (0x100-frac))
					   +(buf_ptr[1] * frac)) >> 8;
		}
		buf_ptr+=2;

		if (netanimno[1] == buf_ptr[0])
		{
			netframe[1] = ((netframe[1] * (0x100-frac))
					   +(buf_ptr[1] * frac)) >> 8;
		}
		buf_ptr+=2;


		/*
		ik_blend_val = ((ik_blend_val * (0x100-frac))
				   +(*buf_ptr++ * frac)) >> 8;

		ik_e = ((ik_e * (0x100-frac))
				   +(*buf_ptr++ * frac)) >> 8;
		ik_p = ((ik_p * (0x100-frac))
			  +(*buf_ptr++ * frac)) >> 8;
		ik_r = ((ik_r * (0x100-frac))
			  +(*buf_ptr++ * frac)) >> 8;
		ik_h = ((ik_h * (0x100-frac))
			  +(*buf_ptr++ * frac)) >> 8;
		  */
	}

	for (plyr=player; plyr<&ball; plyr++)
	{
		if (plyr->top_blend_ctr != 0x100)	plyr->top_blend_ctr &= 0xff;
		if (plyr->leg_blend_ctr != 0x100)	plyr->leg_blend_ctr &= 0xff;
	}


	HotSpot_Load( (s32 *)&HotSpotBuf );
	Clouds_Load( (s32 *)&CloudsBuf );

	bhp = &player[ball_handler];
}


#ifdef PC
 static TXTCEL_T *rp_txt_cel;
 static TXTCEL_T *rp_txt_cel1;
 static TXTCEL_T *rp_txt_cel2;
#endif


void init_replay_buf(void)
{
	if (replay_buf)
		return;

	replay_buf=MemMalloc(4*REP_BUF_WID*REP_BUF_SZ, MEM_ANY);

	replay_frame =
	top_frame    = 0;
	replay_wrap_flag = FALSE;
}


void free_replay_buf(void)
{
	if (replay_buf)
	{
		MemFree(replay_buf);
		replay_buf = NULL;
	}
}



void start_replay(int pad)
{
	// No auto-replays in practice mode
	if (pad==-1 && PracticeMode)
		return;

#ifdef PC
//	if (pad==-1)	return;
#endif

	if ((top_frame == 0) & (!replay_wrap_flag))
	{
		//don't allow replay before a frame is saved
		return;
	}

	pf_lock=FALSE;

	not_switched_yet = TRUE;
	forward_flag	 = 0;
	SfxFrame = -1;

#ifndef ML
	if (possession_flag)
	{
		replay_player= ball_handler;
	}
	else
	{
		replay_player= last_handler;
	}
#endif

	replay_pad = pad;

	if (pad>=0)
	{
		replay_frame = (top_frame - ONE_SEC) << 8;
	}
	else
	{
		if (cam3_flag)
		{
			not_switched_yet = FALSE;
			CamStartActionReplay(cam3_flag);
			replay_frame = (top_frame - HALF_SEC) << 8;
		}
		else
		{
			replay_frame = (top_frame - 3*ONE_SEC) << 8;
		}
	}


#ifdef DEBUG
	//replay_frame = (top_frame - 1) << 8;
#endif

	if (replay_frame < 0)
	{
		if (!replay_wrap_flag)
		{
			replay_frame = 0;
		}
		else
		{
			replay_frame += REP_BUF_SZ<<8;
			if (top_frame==1 && last_saved_frame==0 && replay_wrap_flag==0 && replay_frame>=top_frame)
				BRK;
		}
	}

	if (cam3_flag==0)		replay_ctr = ONE_SEC/2;
	
	replay_flag = TRUE;

	#if defined(DEBUG)
	{
		char	txt[128];

		sprintf(txt,"bh=%d",ball_handler);
		db_out(start_replay,txt);
	}
	#endif
}



void end_replay(int pad)
{
	int	i;

	if (pad!=replay_pad)
		return;

	if (!replay_flag)
		return;

	#ifdef PC
	delete_text_cel( rp_txt_cel);
	FreeTxtCel(rp_txt_cel);
	delete_text_cel( rp_txt_cel1);
	FreeTxtCel(rp_txt_cel1);
	delete_text_cel( rp_txt_cel2);
	FreeTxtCel(rp_txt_cel2);
	#endif

	#if defined(DEBUG)
	{
		char	txt[128];

		sprintf(txt,"bh=%d",ball_handler);
		db_out(end_replay,txt);
	}
	#endif

	replay_flag = FALSE;
	copy_back_vars();

	#if defined(DEBUG)
	{
		char	txt[128];

		sprintf(txt,"bh=%d",ball_handler);
		db_out(end_replay,txt);
	}
	#endif


	last_dunker = -1;

	replay_pad = -1;

	bhp=&player[ball_handler];
	pf_lock=FALSE;

	for (i=1; i<5; i++)
	{
		ctrlrT	*ctrl=&ctrlrs[i];

		ctrl->c_mask=-1;
		ctrl->r_mask=-1;
		ctrl->db_ctrlr=0;
		ctrl->mctrlr=0;
	}
}


void advance_frame(void)
{
	top_frame++;
	if (top_frame == REP_BUF_SZ)
	{
		top_frame		= 0;
		replay_wrap_flag= TRUE;
	}
}


bool end_frame_test(int f)
{
	if (last_saved_frame)
	{
		if (f == ((last_saved_frame<<8)-1))	return TRUE;
		return (f == ( ((last_saved_frame-4) % REP_BUF_SZ)<<8) );
	}
	else
	{
#ifdef DEBUG
			{
			char text[128],numF[16],numB[16];

			IntToHexStr(f,numF);
			IntToHexStr(REP_BUF_SZ<<8,numB);

			sprintf(text,"%s/%s\n",numF,numB);
			PCwrite(-1, text, strlen(text));
			}
#endif

		if (replay_pad==-1)
			return ( (f==((REP_BUF_SZ-1)<<8)) || f==0);

		return (f==((REP_BUF_SZ-1)<<8));
	}
}



void process_replay(void)
{
int fr;
int test_frame;

#ifdef PC
    if (KernelGetKeyStatus(KEY_F1) & 1)			replay_player = 0;
    else if (KernelGetKeyStatus(KEY_F2) & 1)	replay_player = 1;
    else if (KernelGetKeyStatus(KEY_F3) & 1)	replay_player = 2;
    else if (KernelGetKeyStatus(KEY_F4) & 1)	replay_player = 3;
    else if (KernelGetKeyStatus(KEY_F5) & 1)	replay_player = 4;
    else if (KernelGetKeyStatus(KEY_F6) & 1)	replay_player = 5;
    else if (KernelGetKeyStatus(KEY_F7) & 1)	replay_player = 6;
    else if (KernelGetKeyStatus(KEY_F8) & 1)	replay_player = 7;
    else if (KernelGetKeyStatus(KEY_F9) & 1)	replay_player = 8;
    else if (KernelGetKeyStatus(KEY_0) & 1)		replay_player = 9;

	//test blocked path and clear pass

#endif


	if (!pf_lock)
	{
		int	i;

		for (i=0; i<4; i++)
			cf[i]=controls[i];

		pf_lock=TRUE;
		copy_vars();
	}

	//auto replay
	if (replay_pad== -1)
	{
		int c;

		if (not_switched_yet)
		{
			if (bhp->zco)
			{
				CamSwitchActionReplay();
				not_switched_yet = FALSE;
			}
		}


		if (bhp->control)
		{
			c = test_and_mask(ctrlrs+bhp->control, CTRL_FIRE_A+CTRL_START);
		}
		else
		{
			c = test_and_mask(ctrlrs+1, CTRL_FIRE_A+CTRL_START) |
				test_and_mask(ctrlrs+2, CTRL_FIRE_A+CTRL_START) |
				test_and_mask(ctrlrs+3, CTRL_FIRE_A+CTRL_START) |
				test_and_mask(ctrlrs+4, CTRL_FIRE_A+CTRL_START);
		}

		if (c)	replay_ctr = 0;

		ctrlrs[0].ctrlr = CTRL_RIGHT + (4<<24);

		if ( (c) || (end_frame_test(replay_frame)) )
		{
			ctrlrs[0].ctrlr = 0;
			replay_ctr--;
			if (replay_ctr<=0)
			{
				end_replay(-1);

				switch (cam3_flag)
				{
				case 1:
					if (bhp->control)
					{
						CtrlMotorOn(bhp->control-1, 10);
					}
//old					AudioPlaySfx(SFX_ID_DUNK1, 22050, 255, 7);
//old					AudioPlaySfx(SFX_ID_DUNK1, 22050, 255, 7);
					cam3_flag = 2;
					start_replay(-1);
					break;

				case 2:
					if (bhp->control)
					{
						CtrlMotorOn(bhp->control-1, 20);
					}
//old					AudioPlaySfx(SFX_ID_DUNK1, 22050, 255, 7);
//old					AudioPlaySfx(SFX_ID_DUNK1, 22050, 255, 7);
//old					AudioPlaySfx(SFX_ID_DUNK1, 22050, 255, 7);
//old					AudioPlaySfx(SFX_ID_DUNK1, 22050, 255, 7);
					cam3_flag = 0;
					CamStopActionReplay();
					break;

				default:
					CamStopActionReplay();
				}

				return;
			}
		}
	}
	else
	{
		if (test_and_mask(ctrlrs+replay_pad+1, CTRL_TRIGGER))
		{
			forward_flag ^= 1;
		}
	}



    if(ctrlrs[replay_pad+1].ctrlr & CTRL_LEFT)
    {
		forward_flag = 0;
		fr = replay_frame;
		replay_frame -= ((ctrlrs[replay_pad+1].ctrlr>>24) & 0xf) << 5;

		if (!replay_wrap_flag && replay_frame<0)
			replay_frame=0;

		//have i gone through end of stored action?
		test_frame = (last_saved_frame+1)<<8;
		if (test_frame > (REP_BUF_SZ<<8))
		{
			test_frame -= REP_BUF_SZ<<8;
		}

		if ( ((replay_frame - test_frame) ^ (fr - test_frame)) < 0)
		{
			replay_frame = test_frame;
		}

		if (replay_frame < 0)
		{
			if (replay_wrap_flag)	replay_frame += REP_BUF_SZ<<8;
			else					replay_frame = 0;
		}
    }



	if (forward_flag)
	{
		ctrlrs[replay_pad+1].ctrlr = CTRL_RIGHT+(8<<24);
	}

	if(ctrlrs[replay_pad+1].ctrlr & CTRL_RIGHT)
    {
		fr = replay_frame;
		replay_frame += ((ctrlrs[replay_pad+1].ctrlr>>24) & 0xf) << 5;

		if (!replay_wrap_flag && replay_frame>last_saved_frame<<8)
			replay_frame=last_saved_frame<<8;

		//have i gone through end of stored action?
		if ( (((replay_frame>>8) - last_saved_frame) ^ ((fr>>8) - last_saved_frame)) < 0)
		{
			forward_flag = 0;
			replay_frame = (last_saved_frame) ? ((last_saved_frame<<8)-1) : (REP_BUF_SZ<<8)-1;
		}

		if ( replay_frame >= (REP_BUF_SZ<<8) )
		{
			replay_frame -= REP_BUF_SZ<<8;
		}
	}

	load_replay(replay_frame);

	move_camera();

	if (!manualReplayFlag && SfxFrame != (replay_frame >> 8))
	{
		SfxFrame = (replay_frame >> 8);

		ProcessSfx();
	}


#ifdef PC
    sprintf(string, "rframe %x", replay_frame);

    if (rp_txt_cel)       delete_text_cel( rp_txt_cel);
    rp_txt_cel = add_text_cel(string, 10, 420);

	legAnims[0].name = "NULL";

//	replay_player = 0;
//	player[0].top_blend_ctr = 0;

    sprintf(string, "anim %c%s %c%s, frame %x %x, bc %x, rate %x ik %x",
					(player[replay_player].top_anim >= 0) ? ' ':'-',
					playerAnims[abs (player[replay_player].top_anim)].name,
					(player[replay_player].last_top_anim >= 0) ? ' ':'-',
					playerAnims[abs (player[replay_player].last_top_anim)].name,
					player[replay_player].top_frame,
					player[replay_player].last_top_frame,
					player[replay_player].top_blend_ctr,
					player[replay_player].top_rate,
					player[replay_player].ikBlend
					);

//	sprintf(string, "force %d, d_angle %x, ls %x", player[1].force, player[1].d_angle & 0xffff, player[1].last_speed);
    if (rp_txt_cel1)	delete_text_cel( rp_txt_cel1);
    rp_txt_cel1 = add_text_cel(string, 10, 440);

    sprintf(string, "anim %c%s %c%s, frame %x %x, bc %x, rate %x",
					(player[replay_player].leg_anim >= 0) ? ' ':'-',
					legAnims[abs (player[replay_player].leg_anim)].name,
					(player[replay_player].last_leg_anim >= 0) ? ' ':'-',
					legAnims[abs (player[replay_player].last_leg_anim)].name,
					player[replay_player].leg_frame,
					player[replay_player].last_leg_frame,
					player[replay_player].leg_blend_ctr,
					player[replay_player].leg_rate
					);

    if (rp_txt_cel2)	delete_text_cel( rp_txt_cel2);
    rp_txt_cel2 = add_text_cel(string, 10, 460);

#endif

#if defined(NOAH) && 1

	// Print out information about a selected individual
	if (camReplayTargetNo!=-1)
	{
		playert	*plyr=&player[camReplayTargetNo];

		sprintf(idString,"Player #%1d: %s %s",camReplayTargetNo,plyr->plyrROM->pcz__pCName,plyr->plyrROM->pcz__pSName);
		sprintf(mrString,"Main: %s",main_names[plyr->mr_no]);
		sprintf(tpString," Top: %s",top_names[plyr->top_no]);
		sprintf(lgString," Leg: %s",leg_names[plyr->leg_no]);
	}
	else
	{
		*idString=0;
		*mrString=0;
		*tpString=0;
		*lgString=0;
	}

#endif

}


int get_replay_frame(void)
{
	return replay_frame;
}


void Replay_SaveSfx( const u32 id, const u8 volume )
{
	SfxId = id+1;
	SfxVol = volume;
}


#if 0

void SaveReplay()
{
	static s32
		held = 0,
		count = 0;
	char
		filename[] = "C:/Replay00.BUF";
	int
		fd;
	s32
		i,
		f;

	if ((ctrlrs[3].ctrlr & CTRL_SHOULDER_L) && (ctrlrs[3].ctrlr & CTRL_SHOULDER_R))
	{
		if (!held)
		{
			held = -1;


			filename[9] = '0' + (count / 10);
			filename[10] = '0' + (count % 10);
			count++;

			fd = PCcreat( filename, 0 );

			f = top_frame;

			for (i=0; i<REP_BUF_SZ; i++)
			{
				PCwrite( fd, &replay_buf[f*REP_BUF_WID], REP_BUF_WID*4 );

				f = (f + 1) % REP_BUF_SZ;
			}

			PCclose( fd );
		}
	}
	else
		held = 0;
}

#endif


typedef struct SRepCam
{
	s32 Mode		: 4;
	s32 Special	: 6;
	s32	Target	: 5;
	s32	Zoom		: 8;
	s32 Basket	: 1;
	s32 pad			: 8;
} SRepCam;


s8
	AttractPtr,
	AttractBasket = 0;


static SRepCam
	Cameras[300];

static u32
	Speed,
	NumFrames;


void LoadReplay( s32 which )
{
	if (RunAttract)
	{
		FH
			file;
		int
			rep_off,
			cam_off,
			rep_size,
			cam_size;

		file = FileOpen( "Attract.DIR", "rb" );

		FileSeek( file, 4+(which*2*2*4), SEEK_SET );

		FileRead( &rep_off, 4, 1, file );
		FileRead( &rep_size, 4, 1, file );
		FileRead( &cam_off, 4, 1, file );
		FileRead( &cam_size, 4, 1, file );

		FileSeek( file, rep_off, SEEK_SET );
		FileRead( replay_buf, rep_size, 1, file );

		FileSeek( file, cam_off, SEEK_SET );
		FileRead( Cameras, cam_size, 1, file );

		FileClose( file );

		NumFrames = (cam_size / sizeof( SRepCam ));
	}
#if 0
	else
	{
		int
			fd;
		char
			filename[] = "C:/BBall2/Attract/Replay02.BUF";
		s32
			i;

//		filename[24] = '0' + (which / 10);
//		filename[25] = '0' + (which % 10);

		fd = PCopen( filename, 0 );

		for (i=0; i<REP_BUF_SZ; i++)
			PCread( fd, &replay_buf[i*REP_BUF_WID], REP_BUF_WID*4 );

		PCclose( fd );

		NumFrames = REP_BUF_SZ;
		top_frame = 0;
		replay_frame = 0;
		replay_wrap_flag = TRUE;
	}
#endif
}


extern int camFirstFrame;
extern s32 AttractFrame;


static void SetAttractCam( s32 frame )
{
	SRepCam
		*camera;

	frame >>= 8;

	camera = &Cameras[frame];

	if (frame == 0 ||
			camMode != camera->Mode ||
			camSpecialMode != camera->Special)
	{
		camFirstFrame = TRUE;
		camSpecialFirstTime = TRUE;
		camSpecialCut = TRUE;
	}

	camMode					= camera->Mode & 15;
	camSpecialMode	= camera->Special;
	camTargetNo			= camera->Target;
	camZoomUser			= camera->Zoom & 255;
	AttractBasket		= camera->Basket;
}


#define C_LOAD	0
#define C_TEXT	1
#define C_END		2


void ProcessAttract()
{
	static struct
	{
		s8
			cmd,
			arcade;
		s16
			speed,
			file;
	} list[] =
		{
// Dunks...
			{ C_TEXT },
			{ C_LOAD,	FALSE,	192,	 0 },
			{ C_LOAD,	FALSE,	192,	 1 },
			{ C_LOAD,	FALSE,	192,	 2 },
			{ C_LOAD,	FALSE,	192,	 3 },
// Faces...
			{ C_TEXT },
			{ C_LOAD,	FALSE,	192,	 4 },
			{ C_LOAD,	FALSE,	128,	 5 },			// so
			{ C_LOAD,	FALSE,	192,	 6 },
			{ C_LOAD,	FALSE,	128,	 7 },			// sp
			{ C_LOAD,	FALSE,	192,	 8 },
// Arcade...
			{ C_TEXT },
			{ C_LOAD,	TRUE,		192,	 9 },
			{ C_LOAD,	TRUE,		192,	10 },
			{ C_LOAD,	TRUE,		192,	11 },
			{ C_LOAD,	TRUE,		192,	12 },
// Kobe...
			{ C_TEXT },
			{ C_LOAD,	FALSE,	192,	13 },
			{ C_LOAD,	FALSE,	192,	14 },
			{ C_LOAD,	FALSE,	150,	15 },
// Signatures...
			{ C_TEXT },
			{ C_LOAD,	FALSE,	192,	16 },
			{ C_LOAD,	FALSE,	192,	17 },
			{ C_LOAD,	FALSE,	192,	18 },			// ai
			{ C_LOAD,	FALSE,	150,	19 },
			{ C_TEXT },
			{ C_END  },
		};
	s32
		done = FALSE;

	if (AttractFrame < 0)
	{
		while (!done)
		{
			switch (list[AttractPtr].cmd)
			{
				case C_TEXT:
Gfx_Wait();
Floor_Deinit();
					ShowAttractText();
Floor_Init( dfb );
					break;

				case C_LOAD:
					LoadReplay( list[AttractPtr].file );
					Speech_Attract( -list[AttractPtr].file - 1 );

					tip_off_flag = FALSE;

					Speed = list[AttractPtr].speed;
					ArcadeMode = list[AttractPtr].arcade;

					done = TRUE;
					break;

				case C_END:
					AttractPtr = -1;
					currentProcess->status = PROCESS_FREEING;
					return;
			}

			AttractPtr++;
		}

		AttractFrame = 0;
		SfxFrame = -1;
	}

	load_replay( AttractFrame );
	Speech_Attract( AttractFrame >> 8 );

	if (SfxFrame != (AttractFrame >> 8))
	{
		SfxFrame = (AttractFrame >> 8);

		ProcessSfx();
	}

	if (RunAttract)
	{
		SetAttractCam( AttractFrame );

		AttractFrame += Speed;

		if ((AttractFrame>>8) >= (NumFrames-1))
			AttractFrame = -1;
	}

	move_camera();
}
