/*************************************************************************
 *
 * anim.c
 *
 *************************************************************************
 *
 * animation processor
 *
 *************************************************************************/

/*
 * includes
 */

//#include <string.h>
#include "ult_64.h"
#include "anim.h"
#include "define.h"
#include "ml.h"
#include "file.h"


extern OSPiHandle
	*cartRomHandle;


/*
 * defines
 */
//#define	OLDBLEND

#define COMP_3TO1	1
#define COMP_SCALE16	2
#define COMP_ALLZERO	4

#define BLEND(a,b,t,r) { signed long __blendT1,__blendT2; 		\
		       __blendT1=(signed long)(a);			\
		       __blendT2=(signed long)(b);			\
		       __blendT1+=((__blendT2-__blendT1)*(signed long)(t))/256;	\
		       (r)=__blendT1; }

#if defined(OLDBLEND)
#define BLENDANG(a,b,t,r) { signed short __blendT1,__blendT2,__blendT3; 		\
		            __blendT1=(signed short)(a);			\
		            __blendT2=(signed short)(b);			\
			    __blendT3=__blendT2-__blendT1;			\
		            __blendT1+=(__blendT3*(signed long)(t))/256;	\
		            (r)=__blendT1; }
#else
#undef	BLENDANG
//#define BLENDANG(a,b,t,r) (r)=((t)<128) ? (a) : (b)
#define BLENDANG(a,b,t,r) (r)=blendang(a,b,t)
#define	AM 65536

SW blendang
(short a, short b, unsigned short t)
{
	int	d;
	int	flag=FALSE;

	d=b-a;
	if (d>AM/2)
		d-=AM;
	else if (d<-AM/2)
		d+=AM;

	d=(d*t)>>8;
	
	return a+d;
}

#endif

#define UPPER_NODES	10
#define LOWER_NODES	7
#define FULL_NODES	17
#define BALL_NODES	1

#define UPPER_ENTRIES	(8*4)
#define LOWER_ENTRIES	(8*4)
#define FULL_ENTRIES	(4*4)
#define BALL_ENTRIES	(2*4)

#define BLOCK_FRAMES 8//16

/*
 * typedefs
 */

/*
 * structures
 */

/*
 * globals
 */

int lowerTags[]={	0,11,12,13,14,15,16	};
int upperTags2[]={	1,2,3,4,5,6,7,8,9,10};
int upperTags[]={	1,2,3,4,5,6,7,8,9,10};
int fullTags[]={	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16	};
int ballTags[]={	15	};
int *tagTable;
int *tagTablePtrs[]=
{
	NULL,			// 0
	ballTags,		// 1
	NULL,			// 2
	NULL,			// 3
	NULL,			// 4
	NULL,			// 5
	NULL,			// 6
	lowerTags,		// 7
	upperTags,		// 8
	NULL,			// 9
	upperTags2,		// 10
	NULL,			// 11
	NULL,			// 12
	NULL,			// 13
	NULL,			// 14
	fullTags		// 15
};

animCache	*upperCache;
animCache	*lowerCache;
animCache	*fullCache;
animCache	*ballCache;
int		cacheCurrentTime;
int		cacheOpened=0;


static char mirror[MAX_BONES]={
NODE_HIPS,NODE_CHEST,NODE_HEAD,
NODE_RSHOULDER, NODE_RBICEP, NODE_RFORARM, NODE_RHAND,
NODE_LSHOULDER, NODE_LBICEP, NODE_LFORARM, NODE_LHAND,
NODE_RTHIGH, NODE_RSHIN, NODE_RFOOT,
NODE_LTHIGH, NODE_LSHIN, NODE_LFOOT,
};


static OSMesgQueue
	DMAqueue;

static OSMesg
	DMAmesg;


void QuatExpand(Quat *q, SW *a)
{
int c0, s0, c1, s1, c2, s2, s0c2;


	c0 = LFPCos(a[0]);
	s0 = LFPSin(a[0]);

	c1 = LFPCos(a[1]);
	s1 = LFPSin(a[1]);

	c2 = LFPCos(a[2]);
	s2 = LFPSin(a[2]);


	s0c2 = (s0 * c2) >> 14;
	q->w = c0;
	q->x = (s0c2 * c1) >> 14;
	q->y = (s0c2 * s1) >> 14;
	q->z = (s0 * s2) >> 14;
}


/*
 * function prototypes
 */

void AnimDecompressStream(signed short *dest,int frames,unsigned char **psrc,int step);


/*
 * code
 */

void AnglesToQuats(short *bData, short *bData2, int frac, Quat *dst)
{
	Quat	q1,q2;
	SW		s1[3],s2[3];

	s1[0]=bData[0];
	s1[1]=bData[1];
	s1[2]=bData[2];
	QuatExpand(&q1,s1);

	s2[0]=bData2[0];
	s2[1]=bData2[1];
	s2[2]=bData2[2];
	QuatExpand(&q2,s2);

	InterpolateQuat(&q1,&q2,frac,dst);
}



/***************************************************************************
 *
 * void AnimEvaluate(anim *a,long root,long frame,animFrame *f)
 *
 ***************************************************************************
 *
 * This just copies data into the animFrame buffer - may interpolate in the
 * fullness of time...
 * Note that the animFrame must be big enough to accomodate offsets for
 * the skeleton that the animation has been bound to.
 *
 ***************************************************************************/

void AnimEvaluate(anim *a,long root,long frame,animFrame *f)
{
	int i,j;
	short *wData;
	short *wData2;
	short *bData;
	short *bData2;
	long bpf;
	int frac;


	/*
	 * split frame into integer and fractional part
	 */

	frac=frame & 0xFF;
	frame>>=8;

	/*
	 * get bytes per frame - rounded up
	 */

	bpf=(3*a->numNodes)*sizeof(short) + 3*sizeof(short);
	bpf=(bpf+1)&(~1);

	/*
	 * clamp frame no.
	 */

	if(frame<0) frame=0;
	if(frame>=a->numFrames) frame=a->numFrames-1;

	/*
	 * get pointer to data for frame
	 */

	if(a->flags & ANIM_FLAG_COMPRESSED)
		wData=AnimCache(a,frame);
	else
	{
//		wData=(short *)(a->data+(bpf*frame));
		wData=AnimCache(a,frame);
	}

	bData=(short *)(wData+3);

	if(frac)
	{
		if(frame!=a->numFrames-1)
			wData2=AnimCache(a,frame+1);
		else
			wData2=AnimCache(a,0);
		bData2=(short *)(wData2+3);
	}

	/*
	 * copy data across - may interpolate eventually
	 */

	if(root)
	{
		if(!frac)
		{
			f->x=wData[0];
			f->y=wData[1];
			f->z=wData[2];
		}
		else
		{
			BLEND(wData[0],wData2[0],frac,f->x);
			BLEND(wData[1],wData2[1],frac,f->y);
			BLEND(wData[2],wData2[2],frac,f->z);
		}
	}

	if(!frac)
	{
	 	for(i=0;i<a->numNodes;i++)
		{
			if(a->flags & ANIM_FLAG_NOTAGS)	j=tagTable[i];
			else							j=a->tags[i];//+a->numNodes];

			QuatExpand(&f->q[j], &bData[i*3+0]);
		}
	}
	else
	{
		SW temp[3];

	 	for(i=0;i<a->numNodes;i++)
		{
			if(a->flags & ANIM_FLAG_NOTAGS)	j=tagTable[i];
			else							j=a->tags[i];//+a->numNodes];
#if defined(OLDBLEND)
			BLENDANG(bData[i*3+0], bData2[i*3+0], frac, temp[0]);
			BLENDANG(bData[i*3+1], bData2[i*3+1], frac, temp[1]);
			BLENDANG(bData[i*3+2], bData2[i*3+2], frac, temp[2]);
			QuatExpand(&f->q[j], temp);
#else
			AnglesToQuats(&bData[i*3],&bData2[i*3],frac,&f->q[j]);
#endif
		}
	}
}


/***************************************************************************
 *
 * void AnimEvaluateBlend(anim *a,long root,long frame,animFrame *f,int weighting)
 *
 ***************************************************************************
 *
 * This blends data into the animFrame buffer - may interpolate in the
 * fullness of time...
 * Note that the animFrame must be big enough to accomodate offsets for
 * the skeleton that the animation has been bound to.
 *
 ***************************************************************************/

void AnimEvaluateBlend(anim *a,long root,long frame,animFrame *f,long weighting)
{
	int i,j;
	short *wData;
	short *wData2;
	short *bData;
	short *bData2;
	long bpf;
	long t1,t2;
	long tx,ty,tz;
	short ta,tb,tc;
	int frac;
	Quat q;

	/*
	 * split frame into integer and fractional part
	 */

	frac=frame & 0xFF;
	frame>>=8;

	/*
	 * get bytes per frame - rounded up
	 */

	bpf=(3*a->numNodes)*sizeof(short) + 3*sizeof(short);
	bpf=(bpf+1)&(~1);

	/*
	 * clamp frame no.
	 */

	if(frame<0) frame=0;
	if(frame>=a->numFrames) frame=a->numFrames-1;

	/*
	 * get pointer to data for frame
	 */

	wData=AnimCache(a,frame);
	bData=(short *)(wData+3);

	if(frac)
	{
		if(frame!=a->numFrames-1)
			wData2=AnimCache(a,frame+1);
		else
			wData2=AnimCache(a,0);
		bData2=(short *)(wData2+3);
	}

	/*
	 * blend data in animFrame buffer with new data
	 */

	if(root)
	{
		if(!frac)
		{
			tx=wData[0];
			ty=wData[1];
			tz=wData[2];
		}
		else
		{
			BLEND(wData[0],wData2[0],frac,tx);
			BLEND(wData[1],wData2[1],frac,ty);
			BLEND(wData[2],wData2[2],frac,tz);
		}

		BLEND(f->x,tx,weighting,f->x);
		BLEND(f->y,ty,weighting,f->y);
		BLEND(f->z,tz,weighting,f->z);
	}



	if (!frac)
	{
		for(i=0;i<a->numNodes;i++)
		{

			if(a->flags & ANIM_FLAG_NOTAGS)	j=tagTable[i];
			else							j=a->tags[i];//+a->numNodes];

			QuatExpand(&q, &bData[i*3+0]);
			InterpolateQuat(&f->q[j], &q, weighting, &f->q[j]);
		}
	}
	else
	{
		for(i=0;i<a->numNodes;i++)
		{
			SW temp[3];

			if(a->flags & ANIM_FLAG_NOTAGS)	j=tagTable[i];
			else							j=a->tags[i];//+a->numNodes];

			BLENDANG(bData[i*3+0], bData2[i*3+0], frac, temp[0]);
			BLENDANG(bData[i*3+1], bData2[i*3+1], frac, temp[1]);
			BLENDANG(bData[i*3+2], bData2[i*3+2], frac, temp[2]);
			QuatExpand(&q, temp);
			InterpolateQuat(&f->q[j], &q, weighting, &f->q[j]);
		}
	}
}



/***************************************************************************
 *
 * void AnimEvaluateMirror(anim *a,int32 root,int32 frame,animFrame *f)
 *
 ***************************************************************************
 *
 * Makes animFrame buffer with animation reflected about x axis
 * Note that the animFrame must be big enough to accomodate offsets for
 * the skeleton that the animation has been bound to.
 *
 ***************************************************************************/


void AnimEvaluateMirror(anim *a,long root,long frame,animFrame *f)
{
	int i,j;
	short *wData,*wData2;
	short *bData,*bData2;
	long bpf;
	int frac;

	/*
	 * split frame into integer and fractional part
	 */

	frac=frame & 0xFF;
	frame>>=8;

	/*
	 * get bytes per frame - rounded up
	 */

	bpf=(3*a->numNodes)*sizeof(signed short) + 3*sizeof(short);
	bpf=(bpf+1)&(~1);

	/*
	 * clamp frame no.
	 */

	if(frame<0) frac=frame=0;
	if(frame>=a->numFrames)
	{
		frac = 0;
		frame=a->numFrames-1;
	}

	/*
	 * get pointer to data for frame
	 */

	wData=AnimCache(a,frame);
	bData=(short *)(wData+3);

	if(frac)
	{
		if(frame!=a->numFrames-1)
			wData2=AnimCache(a,frame+1);
		else
			wData2=AnimCache(a,0);
		bData2=(short *)(wData2+3);
	}

	/*
	 * copy data across - may interpolate eventually
	 */

	if(root)
	{
		if(!frac)
		{
			f->x=-wData[0];
			f->y=wData[1];
			f->z=wData[2];
		}
		else
		{
			BLEND(wData[0],wData2[0],frac,f->x);
			BLEND(wData[1],wData2[1],frac,f->y);
			BLEND(wData[2],wData2[2],frac,f->z);
			f->x=-f->x;
		}
	}

	if(!frac)
	{
	 	for(i=0;i<a->numNodes;i++)
		{
			if(a->flags & ANIM_FLAG_NOTAGS)	j=tagTable[i];
			else							j=a->tags[i];//+a->numNodes];

			j=mirror[j];

			QuatExpand(&f->q[j], &bData[i*3]);
			//AnglesToQuatPRH((Quat *)&f->angles[j*4], &bData[i*3]);
			f->q[j].w = -f->q[j].w;
			f->q[j].x = -f->q[j].x;
		}
	}
	else
	{
	 	for(i=0;i<a->numNodes;i++)
		{
			SW temp[3];


			if(a->flags & ANIM_FLAG_NOTAGS)	j=tagTable[i];
			else							j=a->tags[i];//+a->numNodes];

			j=mirror[j];

#if defined(OLDBLEND)
			BLENDANG(bData[i*3+0],bData2[i*3+0],frac,temp[0]);
			BLENDANG(bData[i*3+1],bData2[i*3+1],frac,temp[1]);
			BLENDANG(bData[i*3+2],bData2[i*3+2],frac,temp[2]);
			temp[1] = -temp[1];
			temp[2] = -temp[2];
			QuatExpand(&f->q[j], temp);
			//AnglesToQuatPRH((Quat *)(&f->angles[j*4]), temp);
#else
			AnglesToQuats(&bData[i*3],&bData2[i*3],frac,&f->q[j]);
			f->q[j].w = -f->q[j].w;
			f->q[j].x = -f->q[j].x;
#endif
		}
	}
}




/***************************************************************************
 *
 * void AnimEvaluateBlendMirror(anim *a,long root,long frame,animFrame *f,int weighting)
 *
 ***************************************************************************
 *
 * This blends data refelcted about the x axis into the animFrame buffer
 * Note that the animFrame must be big enough to accomodate offsets for
 * the skeleton that the animation has been bound to.
 *
 ***************************************************************************/

void AnimEvaluateBlendMirror(anim *a,long root,long frame,animFrame *f,long weighting)
{
	int i,j;
	short *wData;
	short *wData2;
	short *bData;
	short *bData2;
	long bpf;
	long t1,t2;
	int tx,ty,tz;
	short ta,tb,tc;
	int frac;

	/*
	 * split frame into integer and fractional part
	 */

	frac=frame & 0xFF;
	frame>>=8;

	/*
	 * get bytes per frame - rounded up
	 */

	bpf=(3*a->numNodes)*sizeof(short) + 3*sizeof(short);
	bpf=(bpf+1)&(~1);

	/*
	 * clamp frame no.
	 */

	if(frame<0) frac=frame=0;
	if(frame>=a->numFrames)
	{
		frame=a->numFrames-1;
		frac =0;
	}

	/*
	 * get pointer to data for frame
	 */

	wData=AnimCache(a,frame);
	bData=(short *)(wData+3);

	if(frac)
	{
		if(frame!=a->numFrames-1)
			wData2=AnimCache(a,frame+1);
		else
			wData2=AnimCache(a,0);
		bData2=(short *)(wData2+3);
	}

	/*
	 * blend data in animFrame buffer with new data
	 */

	if(root)
	{
		if(!frac)
		{
			tx=wData[0];
			ty=wData[1];
			tz=wData[2];
		}
		else
		{
			BLEND(wData[0],wData2[0],frac,tx);
			BLEND(wData[1],wData2[1],frac,ty);
			BLEND(wData[2],wData2[2],frac,tz);
		}

		tx=-tx;

		BLEND(f->x,tx,weighting,f->x);
		BLEND(f->y,ty,weighting,f->y);
		BLEND(f->z,tz,weighting,f->z);
	}

	for(i=0;i<a->numNodes;i++)
	{
		Quat q;
		SW   temp[3];


		if(a->flags & ANIM_FLAG_NOTAGS)	j=tagTable[i];
		else							j=a->tags[i];//+a->numNodes];
		j=mirror[j];

		if(!frac)
		{
			QuatExpand(&q, &bData[i*3+0]);
			//AnglesToQuatPRH(&q, &bData[i*3+0]);

		}
		else
		{
#if defined(OLDBLEND)
			BLENDANG(bData[i*3+0],bData2[i*3+0],frac,temp[0]);
			BLENDANG(bData[i*3+1],bData2[i*3+1],frac,temp[1]);
			BLENDANG(bData[i*3+2],bData2[i*3+2],frac,temp[2]);
			QuatExpand(&q, temp);
			//AnglesToQuatPRH(&q, temp);
#else
			AnglesToQuats(&bData[i*3],&bData2[i*3],frac,&q);
#endif
		}

		q.w = -q.w;
		q.x = -q.x;
		InterpolateQuat(&f->q[j], &q, weighting, &f->q[j]);
	}
}








/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

animCache *AnimCacheCreate(int numNodes,int numEntries)
{
	animCache
		*c;
	s32
		bpe,
		bpf,
		size;

	/*
	 * allocate and clear cache
	 */

	size = (numNodes + 1) * 3 * 2;
	size = (size + 1) & ~1;			// Pad to 2 bytes.
	bpf = size;

	size *= BLOCK_FRAMES;
	size += sizeof( animCacheEntry ) - 4;
	size = (size + 15) & ~15;			// Pad to 16 bytes for DMA.
	bpe = size;

	size *= numEntries;
	size += sizeof( animCache ) - 4;

	c = (animCache *)MemMalloc( size, MEM_ANY );
#ifdef DEBUG
	if (!c)
	{
		FatalError( "Out of memory!", __FILE__, __LINE__ );
	}
#endif

	memset( c, 0, size );
	c->numNodes = numNodes;
	c->numEntries = numEntries;
	c->bytesPerFrame = bpf;
	c->bytesPerEntry = bpe;

	return( c );
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

void AnimCacheOpen(void)
{
	int i,j,k,l;

	osCreateMesgQueue( &DMAqueue, &DMAmesg, 1 );

	/*
	 * create and clear all caches
	 */

	upperCache=AnimCacheCreate(UPPER_NODES,UPPER_ENTRIES);
	lowerCache=AnimCacheCreate(LOWER_NODES,LOWER_ENTRIES);
	fullCache=AnimCacheCreate(FULL_NODES,FULL_ENTRIES);
	ballCache=AnimCacheCreate(BALL_NODES,BALL_ENTRIES);

	/*
	 * start cache timestamp
	 */

	cacheCurrentTime=0;
	cacheOpened=1;
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

void AnimCacheClose(void)
{
	MemFree(upperCache);
	MemFree(lowerCache);
	MemFree(fullCache);
	MemFree(ballCache);
	cacheOpened=0;
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

#if 0
	static u64
		Whatever[(16*18*3*2)/8];
#endif


static void CopyFromROM( u8 *src, u8 *dst, s32 size )
{
#if 0

	register s32
		i;
	register u8
		*to = (u8 *)dst;

	for (i=0; i<size; i+=4)
		osEPiReadIo( cartRomHandle, (u32)&src[i], (u32 *)&to[i] );

#else

	OSIoMesg
		io_mesg;

	osInvalDCache( dst, size );

	io_mesg.hdr.pri = OS_MESG_PRI_NORMAL;
	io_mesg.hdr.retQueue = &DMAqueue;
	io_mesg.dramAddr = (void *)dst;
	io_mesg.devAddr = (u32)src;
	io_mesg.size = size;

	osEPiStartDma( cartRomHandle, &io_mesg, OS_READ );

	osRecvMesg( &DMAqueue, &DMAmesg, OS_MESG_BLOCK );

#endif
}


int cacheMisses=0,cacheHits=0;

short *AnimCache(anim *a,int frame)
{
	animCache	*c;
	animCacheEntry	*ce,*ceo;
	int		nc;
	int		blockNo,blockOff;
	int		i,j,oldest,oldestTime;
	short		*wptr,*rptr,*ww;
	short		*retPtr;
	int		step,ct;

	/*
	 * set tag table
	 */

	if(a->flags & ANIM_FLAG_NOTAGS)
	{
		tagTable=tagTablePtrs[a->numNodes];
		if(!tagTable) exit(1);
	}

	/*
	 * figure out which cache we want
	 */

	if(a->numNodes==FULL_NODES)		c=fullCache;
	else if(a->numNodes==LOWER_NODES)	c=lowerCache;
	else if(a->numNodes==UPPER_NODES)	c=upperCache;
	else if(a->numNodes==BALL_NODES)	c=ballCache;
	else exit(1);

	/*
	 * is the part of this animation that we want cached ?
	 */

	blockNo=frame / BLOCK_FRAMES;
	blockOff=frame % BLOCK_FRAMES;

	ct=a->numFrames-(blockNo*BLOCK_FRAMES); if(ct>BLOCK_FRAMES) ct=BLOCK_FRAMES;

	if(a->flags & (1 << (blockNo+ANIM_FLAG_BLOCK_SHIFT)))
	{
		/*
		 * praise be! just find that sucker...
		 */

		ce=(animCacheEntry *)(&c->entries[0]);
		for(i=0;i<c->numEntries;i++)
		{
			if(ce->header==a &&
			   ce->startBlock==blockNo)
			{
				cacheHits++;
				ce->timeStamp=cacheCurrentTime++;
			 	return((short *)(&ce->data[c->bytesPerFrame*blockOff]));
			}
			ce=(animCacheEntry *)((char *)ce + c->bytesPerEntry);
		}

		// shouldn't happen!
		while(1);
	}

	/*
	 * right... we need to find the oldest entry
	 */

	ce=(animCacheEntry *)(&c->entries[0]);
	oldestTime=0;
	oldest=0;
	ceo=ce;
	for(i=0;i<c->numEntries;i++)
	{
		j=cacheCurrentTime-ce->timeStamp;
		if(j>oldestTime)
		{
			oldestTime=j;
			oldest=i;
			ceo=ce;
		}
		ce=(animCacheEntry *)((char *)ce + c->bytesPerEntry);
	}

	/*
	 * if this block wasn't empty, remove animation's bit
	 */

	if(ceo->header) ceo->header->flags &= ~(1<<(ceo->startBlock+ANIM_FLAG_BLOCK_SHIFT));

	/*
	 * update header
	 */

	ceo->header=a;
	ceo->timeStamp=cacheCurrentTime++;
	ceo->startBlock=blockNo;

	/*
	 * decompress anim!
	 */

	wptr=(short *)&ceo->data[0];
	retPtr=wptr + (blockOff * c->bytesPerFrame/2);

	if(a->flags & ANIM_FLAG_COMPRESSED)
	{
		unsigned char *rptr;
		unsigned char c;

FatalError( "Compressed anim", __FILE__, __LINE__ );

		// read block offset, get ptr
		if(a->flags & ANIM_FLAG_NOTAGS)
			i=a->tags[blockNo];
		else
			i=*(s32 *)&a->data[blockNo*4];//a->tags[a->numNodes*2+blockNo];

		j=(a->numFrames+(BLOCK_FRAMES-1))/BLOCK_FRAMES;
		rptr=((char *)a) + i;

		// get xlation flag
		if(a->flags & ANIM_FLAG_NOTAGS)
			c=*((unsigned char *)&a->tags[j]);
		else
			c=*((unsigned char *)&a->data[j*4]);//&a->tags[a->numNodes*2+j]);

		// figure out between successive frame's info...
		step=(a->numNodes*3)+3;

		if(c)
		{
			AnimDecompressStream(wptr,ct,&rptr,step);
		}
		else
		{
			ww=wptr;
			for(i=0;i<ct;i++)
			{
				ww[0]=ww[1]=ww[2]=0;
				ww+=step;
			}
		}
		wptr+=3;
		for(i=0;i<a->numNodes;i++)
		{
			AnimDecompressStream(wptr,ct,&rptr,step);
			wptr+=3;
		}
	}
	else
	{
		s32
			off,
			size;

		off = c->bytesPerFrame * (blockNo*BLOCK_FRAMES);
		size = ct * (c->numNodes + 1) * 3*2;

		if ((((u32)a->data) >> 28) != 0x8)
		{
			CopyFromROM( &a->data[off], (u8 *)wptr, size );
		}
		else
		{
			rptr=(short *)&a->data[off];

			for(i=0;i<ct;i++)
			{
				*(wptr++)=*(rptr++);
				*(wptr++)=*(rptr++);
				*(wptr++)=*(rptr++);
				for(j=0;j<c->numNodes;j++)
				{
					*(wptr++)=*(rptr++);
					*(wptr++)=*(rptr++);
					*(wptr++)=*(rptr++);
				}
			}
		}
	}

	/*
	 * update flag
	 */

	a->flags |= (1 << (blockNo+ANIM_FLAG_BLOCK_SHIFT));

	cacheMisses++;

	return(retPtr);
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/

void AnimDecompressStream(signed short *dest,int frames,unsigned char **psrc,int step)
{
#if 0			// phil

	int i,j,k,c;
	signed short current[3];
	extern signed short logTable[];
	unsigned char *src;

	// copy ptr
	src=*psrc;

	// get compression type
	c=*(src++);

	// copy keyframe
	dest[0]=(int)src[0] + ((int)src[1]<<8);
	dest[1]=(int)src[2] + ((int)src[3]<<8);
	dest[2]=(int)src[4] + ((int)src[5]<<8);
	dest+=step;
	src+=6;

	// do rest of frames
	if(c & COMP_ALLZERO)
	{
		for(i=1;i<frames;i++)
		{
			dest[0]=dest[0-step];
			dest[1]=dest[1-step];
			dest[2]=dest[2-step];
			dest+=step;
		}
	}
	else
	{
		if(c & COMP_3TO1)
		{
			for(i=1;i<frames;i++)
			{
				j=src[0];
				j+=(int)src[1] << 8;

				dest[0]=logTable[(((signed long)j << 17) >> 27) & 0xFF];
				dest[1]=logTable[(((signed long)j << 22) >> 27) & 0xFF];
				dest[2]=logTable[(((signed long)j << 27) >> 27) & 0xFF];

				if(c & COMP_SCALE16)
				{
					dest[0]=dest[0]/16 + dest[0-step];
					dest[1]=dest[1]/16 + dest[1-step];
					dest[2]=dest[2]/16 + dest[2-step];
				}
				else
				{
					dest[0]=dest[0] + dest[0-step];
					dest[1]=dest[1] + dest[1-step];
					dest[2]=dest[2] + dest[2-step];
				}
				dest+=step;
				src+=2;
			}
		}
		else
		{
			for(i=1;i<frames;i++)
			{
				if(c & COMP_SCALE16)
				{
					dest[0]=dest[0-step] + ((int)logTable[src[0]]/16);
					dest[1]=dest[1-step] + ((int)logTable[src[1]]/16);
					dest[2]=dest[2-step] + ((int)logTable[src[2]]/16);
				}
				else
				{
					dest[0]=dest[0-step] + ((int)logTable[src[0]]);
					dest[1]=dest[1-step] + ((int)logTable[src[1]]);
					dest[2]=dest[2-step] + ((int)logTable[src[2]]);
				}
				dest+=step;
				src+=3;
			}
		}
	}

	// write src ptr back
	*psrc=src;

#endif
}

/*************************************************************************
 *
 *
 *
 *************************************************************************
 *
 *
 *
 *************************************************************************/
