/***************************************************************************
 *
 * min.c
 *
 ***************************************************************************
 *
 * dummy process
 *
 ***************************************************************************/

#define	BIGNOAH	1
#define NO_DEBUG_STUFF


/*
 * includes
 */

#include "ult_64.h"
#include "defs.h"
#include "ctrl.h"
#include "main.h"
#include "static_s.h"
#include "file.h"
#include "define.h"
#include "ml.h"
#include "camera.h"
#include "cammodes.h"
#include "menu.h"
#include "shell.h"
#include "stats.h"

#include "misc.h"
#include "floor.h"
#include "arrows.h"
#include "banners.h"
#include "caption.h"
#include "stadium.h"
#include "playgfx.h"
#include "ballgfx.h"
#include "overgfx.h"
#include "speech.h"
#include "post.h"
#include "view.h"
#include "fastcode.h"
#include "audio.h"
#include "track.h"
#include "hotspot.h"

#include "playbyplay.h"


#include "res/playerid.h"

extern int SecretModeX;

extern s8
	AttractPtr,
	AttractBasket;


#if defined(NOAH)
	#include "speech.h"
	extern SpeechGuy	Announcer;
#endif

extern dyn
	DBdata[];

extern s32
	FrameTicks,
	FrameTime,
	Capacity,
	VolLevel;

extern int
	whichEnd,
	inFreeThrow,
	freeThrowX,
	ShowFreeThrowNet,
	si___gCurState,
	si___gNxtState;

extern s32
	AudioOverruns,
	AudioBusy,
	AudioMaxCmds;

extern SL
	sl___gInfoPlyrPos;

s32
	philX = 0,
	philY = 0,
	philZ = 0;

extern bool	InfoFlag;

extern dunkT *allowed_dunks[];


extern void
	ShowWaitScreen();


int	DC_Play;
char	*Plays[]=
		{
			"BOX",
			"PERIMETER",
			"HIGH POST",
			"LOW POST",
			"MOTION",
			"ISOLATION",
			"INSIDE TRI",
			"OUTSIDE TRI"
		};

#if defined(NOAH)
	#include "define.h"
	extern int	pbo,pbd,pbf;
	extern int	p4f,p9f;
#endif

extern char *l_names[],*a_names[],*main_names[];

extern char *print_shot(void);
/*
 * defines
 */

#define FOV			33.0
#define NEAR_Z	64.0
#define FAR_Z		16384.0

#define VTX_LIST_SIZE (80*1024)
#define GFX_LIST_SIZE (36*1024)


/*
 * typedefs
 */

typedef enum
{
	HEADNODETYPE_NONE,
	HEADNODETYPE_ROTATE,
	HEADNODETYPE_SCALE_MOVE,
	HEADNODETYPE_MOVE
} EHeadNodeTypes;

typedef union
{
	s32
		Rotate;
	struct
	{
		s8
			x,
			y,
			z;
		u8
			Scale;
	} Move;
} UFrame;

typedef struct SAccessories
{
	u8
		hArmBandT[32*8],
		hArmBandB[32*6],
		hWristBand[32*4],
		hKneeBandT[32*8],
		hKneeBandB[32*9],
		hSock[32*19],
		lArmBandT[16*4],
		lArmBandB[16*3],
		lWristBand[16*2],
		lKneeBandT[16*5],
		lKneeBandB[16*5],
		lSock[16*9];
	RGBA16
		Palettes[4][13];
} SAccessories;

typedef struct SUniform
{
	char
		Type[4],
		Game[4],
		Platform[4];
	u32
		Version,
		Others;
	u16
		Chunks,
		PalSize,
		LoSize,
		HiSize;
} SUniform;

typedef struct SHeadGfx
{
	RGBA16
		HiPal[116],
		ExPal[256];
	u8
		TopTex[32*53],
		FrontTex[32*64],
		BackTex[16*64],
		SideTex[16*64],
		GoatieTex[16*64];
} SHeadGfx;

typedef struct SFaceMake
{
	RGBA16
		HiPal[24],
		ExPal[50],
		TopPal[256];
	u8
		TopTex[32*32];
	RGBA32
		HairTex[128*64];
} SFaceMake;


extern int
	camSpecialMode,
	si___gCamThi,
	si___gCamTheta;

extern long
	sl___gCurMemFree;

extern int
	Num3PtPlayers,
	Human3PtMatchPlayers[2];


/*
 * structures
 */

/*
 * globals
 */

#define MAX_CLOUDS 32

static u8
	*CloudTex;

static s32
	NumClouds;

static vector16
	CloudList[MAX_CLOUDS];


int C3PT=0;		// Play a three point contest instead of a real game

s32
	AttractFrame;

s8
	RunAttract,
	AttractMode,
	ShakeScreen,
	BallTrail,
	ShowTrails = FALSE,
	ShowDebug = FALSE;


s32
	XXX,
	YYY,
	ZZZ;


static s32
	MaxVsize = 0,
	MaxGsize = 0;

static SMeshInfo
	NullMesh = { NULL, NULL };

vector16
        WorkVerts[256+64] __attribute__ ((aligned (16)));


static u8
	ShellRedo = 0,
	HeadStarts[HEAD_NODES];

static u16
	VertStarts[DETAIL_LEVELS][MAX_BONES];

static VertexN64
	*VtxPtr,
	*VtxLists[2];

static Gfx
	*GfxPtr,
	*GfxLists[2];

static u8
	*LowModel = NULL,
	*MediumModel = NULL,
	*HighModel = NULL,
	*ExtremeModel = NULL,
	*ExtremeMtls = NULL,
	*Font = NULL,
	*TestLamps,
	*LampMats,
	*NetMtls,
	*NetData,
	*BigHeadModel;

u8
	*FaceAnims,
	*GameBank;

u32
	SfxVolume = AUDIO_MAX_VOLUME;

static u8
	*LHandNodes[DETAIL_LEVELS][BODY_TYPES][HAND_TYPES],
	*RHandNodes[DETAIL_LEVELS][BODY_TYPES][HAND_TYPES];

static SMeshInfo
	LHandMeshes[DETAIL_LEVELS][BODY_TYPES][HAND_TYPES],
	RHandMeshes[DETAIL_LEVELS][BODY_TYPES][HAND_TYPES];

SPlayerInfo
	PlayerInfos[10];

static u8
	*NetAnim;

static Vtx
	NetVerts[2][2][64];

static Mtx
	StadiumMat,
	LampsMtx;

static SView
	Views[2];

static s8
	SplitScreen,
	UseOtherUniform;


static Vp
	vpFull =
	{
//		0, 0, G_MAXZ/2, 0,	/* scale */
//		0, 0, G_MAXZ/2, 0,	/* translate */
		0, 0, G_MAXZ, 0,	/* scale */
		0, 0, 0, 0,	/* translate */
	},
	vpLeft =
	{
		0, 0, G_MAXZ/2, 0,	/* scale */
		0, 0, G_MAXZ/2, 0,	/* translate */
	},
	vpRight =
	{
		0, 0, G_MAXZ/2, 0,	/* scale */
		0, 0, G_MAXZ/2, 0,	/* translate */
	};


/*
 * function prototypes
 */

static Gfx *DrawString(Gfx *gp, const char *string, s32 x, s32 y);

void Net_Init();
void Net_Deinit();


/*
 * code
 */


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

extern u8
	VideoMode;


static s32
	Mpos = 136,
	Halt = TRUE;


void FatalError( const char *error, const char *file, const s32 line )
{
	char
		text[128],
		linestr[16];
	u16
		c;
	s32
		i,
		x,
		y,
		w,
		s,
		t,
		pos,
		cnt;

	memset( linestr, 0, 16 );
	IntToDecStr( line, linestr );
	sprintf( text, "%s (file: %s, line: %s)\n", error, file, linestr );

#ifdef DEBUG

	PCwrite( -1, text, strlen( text ) );

	if (Halt)
		i = *((int *)0x1);			// Crash!

#else

	if (!ShowDebug)
	{
		while (Halt);
		return;
	}

	if (!Font)
		Font = AllocAndLoadFile( "PHIL.FNT", MEM_ANY );

	{
		if (VideoMode == OS_VI_NTSC_LAN1)
			w = 512;
		else
			w = SCR_X_RES;

		osViSetMode( &osViModeTable[VideoMode] );
		osViSetSpecialFeatures( OS_VI_GAMMA_OFF | OS_VI_DIVOT_ON | OS_VI_DITHER_FILTER_ON );


		pos = Mpos;
		cnt = 0;

		i = 0;
		while (text[i])
		{
			c = text[i];
			if (c >= ' ')
			{
				if (c >= 'a' && c <= 'z')
					c -= ('a' - 'A');

				c -= ' ';
				s = (s32)(c & 15) * 8;
				t = (s32)(c / 16) * 8;

				for (y=0; y<8; y++)
				{
					for (x=0; x<8; x++)
					{
						c = Font[(t*128)+s+x] >> 3;
						c = (c << 11) | (c << 6) | (c << 1);

						cfb[0][(((y<<1)+pos+0) * w) + (cnt*8) + x + 32] = c;
						cfb[1][(((y<<1)+pos+0) * w) + (cnt*8) + x + 32] = c;
						cfb[0][(((y<<1)+pos+1) * w) + (cnt*8) + x + 32] = c;
						cfb[1][(((y<<1)+pos+1) * w) + (cnt*8) + x + 32] = c;
					}

					t++;
				}
			}

			i++;

			cnt++;
			if (cnt >= 50)
			{
				cnt = 0;
				pos += 16;
			}
		}
	}

	osWritebackDCacheAll();
	while (Halt);

#endif
}


void _BlastMessage( const char *error, const char *file, const s32 line )
{
	Halt = FALSE;
	Mpos = 16;

	FatalError( error, file, line );

	Halt = TRUE;
	Mpos = 136;
}


extern long
	cont[4];

void BreakMessage( const char *error, const char *file, const s32 line )
{
	s32
		wait = TRUE;

	Halt = FALSE;
	FatalError( error, file, line );
	Halt = TRUE;


	while (wait)
	{
		VBL_Wait();
		ReadControllers();

		if (cont[0] & CTRL_SHOULDER_L &&
				cont[0] & CTRL_SHOULDER_R)
			wait = FALSE;
	}
}


/******************************************************************************/

void PauseAll( const s32 pause )
{
	Crowd_Pause( pause );
	Speech_Pause( pause );
}


/******************************************************************************/

#define KERN_FULL	0
#define KERN_TOP	1
#define KERN_BOT	2
#define KERN_NONE	3


typedef struct
{
	u8
		x1,
		y1,
		x2,
		y2;
} SLine;

typedef struct
{
	u8
		Count,
		pad1;
	u16
		Offset;
	u8
		KernLeft,
		KernRight;
	u16
		pad2;
} SLetter;


static u8
	*Big,
	*Overlay,
	*LineFont;


static u32
	nr,
	ng,
	nb;


static void setcolor( u32 r, u32 g, u32 b )
{
	nr = r;
	ng = g;
	nb = b;
}


static void ApplyOverlay( u8 *src, u8 *dst, RGBA16 *pal )
{
	u32
		sr,
		sg,
		sb,
		dr,
		dg,
		db,
		sa,
		da,
		size = (64*32);

	while (size)
	{
		sr = pal[*dst].r;
		sg = pal[*dst].g;
		sb = pal[*dst].b;

		sa = *src++;

		if (sa)
		{
			da = 255 - sa;

			dr = ((sa * nr) + (sr * da)) >> 8;
			dg = ((sa * ng) + (sg * da)) >> 8;
			db = ((sa * nb) + (sb * da)) >> 8;

			if (sr != dr &&
					sg != dg &&
					sb != db)
			{
				*dst = Pal_FindColor( dr, dg, db, &pal[144], 256-144 ) + 144;
			}
		}

		dst++;
		size--;
	}
}


static void SuperPlot( s32 x, s32 y )
{
	if (x >= 0 && x < (64*4) &&
			y >= 0 && y < ((32+32)*4))
		Big[(y*64*4)+x] = 255;
}


static void BigPlot( s32 x, s32 y )
{
	s32
		w,
		h;

	for (h=-(4/2); h<(4/2); h++)
	{
		for (w=-(4/2); w<(4/2); w++)
			SuperPlot( x+w, y+h );
	}
}


static u8 SuperSample( s32 x, s32 y )
{
	s32
		w,
		h;
	u32
		s = 0;

	x *= 4;
	y *= 4;

	for (h=0; h<4; h++)
	{
		for (w=0; w<4; w++)
			s += (u32)Big[((y+h) * 64 * 4) + (x+w)];
	}

//	s /= (4 * 4);
	s >>= 4;

	return( (u8)s );
}


static void SampleDown()
{
	s32
		x,
		y;

	for (y=0; y<(32+32); y++)
	{
		for (x=0; x<64; x++)
		{
			Overlay[(y*64)+x] = SuperSample( x, y );
		}
	}
}


static void DrawLine( s32 x1, s32 y1, s32 x2, s32 y2 )
{
	s32
		x,
		y,
		dx,
		dy;

	x1 >>= 1;
	y1 >>= 1;
	x2 >>= 1;
	y2 >>= 1;


	if (x1 > x2)
	{
		dx = x1;
		x1 = x2;
		x2 = dx;

		dy = y1;
		y1 = y2;
		y2 = dy;
	}


	dx = abs( x1 - x2 );
	dy = abs( y1 - y2 );

	if (dx == 0 && dy == 0)
		return;

	if (y2 < y1)
	{
		if (dx >= dy)
		{
			dy = 0;
			while (x1 <= x2)
			{
				y = y1 + (dy * (y2 - y1) / dx);

				BigPlot( x1, y );

				x1++;
				dy++;
			}
		}
		else
		{
			dx = 0;
			while (y1 >= y2)
			{
				x = x1 + (dx * (x2 - x1) / dy);

				BigPlot( x, y1 );

				y1--;
				dx++;
			}
		}
	}
	else
	{
		if (dx >= dy)
		{
			dy = 0;
			while (x1 <= x2)
			{
				y = y1 + (dy * (y2 - y1) / dx);

				BigPlot( x1, y );

				x1++;
				dy++;
			}
		}
		else
		{
			dx = 0;
			while (y1 <= y2)
			{
				x = x1 + (dx * (x2 - x1) / dy);

				BigPlot( x, y1 );

				y1++;
				dx++;
			}
		}
	}
}


static void DrawLetter( SLetter *letter, s32 x, s32 y, s32 angle, s32 scale )
{
	matrix
		mat;
	vector16
		v1,
		v2;
	SLine
		*line;
	s32
		i,
		x1,
		y1,
		x2,
		y2,
		w = 0,
		h = 0;

	v1.z = 0;
	v2.z = 0;

	MakeMatPRH( &mat, 0, angle*32, 0 );

	line = (SLine *)&LineFont[letter->Offset];

	for (i=0; i<letter->Count; i++)
	{
		x1 = line->x1 * scale;
		y1 = line->y1 * scale;
		x2 = line->x2 * scale;
		y2 = line->y2 * scale;

		if (x1 > w) w = x1;
		if (x2 > w) w = x2;

		if (y1 > h) h = y1;
		if (y2 > h) h = y2;

		line++;
	}


	w += scale;
	h = (6 * scale);
	line = (SLine *)&LineFont[letter->Offset];

	for (i=0; i<letter->Count; i++)
	{
		x1 = line->x1 * scale;
		y1 = line->y1 * scale;
		x2 = line->x2 * scale;
		y2 = line->y2 * scale;

		v1.x = (x1 - (w/2));
		v1.y = (y1 - h);
		Vec16MulMat( &v1, &mat, &v1 );

		v2.x = (x2 - (w/2));
		v2.y = (y2 - h);
		Vec16MulMat( &v2, &mat, &v2 );

		DrawLine( v1.x+x, v1.y+y, v2.x+x, v2.y+y );

		line++;
	}
}


static s32 GetWidth( SLetter *letter )
{
	SLine
		*line;
	s32
		i,
		w = 0;

	line = (SLine *)&LineFont[letter->Offset];

	for (i=0; i<letter->Count; i++)
	{
		if (line->x1 > w) w = line->x1;
		if (line->x2 > w) w = line->x2;

		line++;
	}

	return( w + 1 );
}


static void Font_Draw( const char *string )
{
	s32
		kern = KERN_NONE;
	SLetter
		*letter;
	s32
		i,
		x,
		y,
		w,
		h,
		scale,
		gap = 0,
		width = 0,
		angle = 0,
		posz[16],
		widths[16];

	i = 0;
	while (string[i])
	{
		letter = (SLetter *)&LineFont[4*4];
		letter += string[i] - ' ';

		if (kern != KERN_NONE)
		{
			if ((kern == KERN_TOP && letter->KernLeft == KERN_BOT) ||
					(kern == KERN_BOT && letter->KernLeft == KERN_TOP))
				gap = 0;
			else
				gap = 1;
		}

		posz[i] = width + gap;

		w = GetWidth( letter );
		widths[i] = w;
		width += gap + w;

		kern = letter->KernRight;
		i++;
	}

	if (!width)
		return;


	h = 281 - (width * 5 / 3);
	if (h < (22*8))
		h = (22*8);


	scale = (64 - width) / 10;
	scale += 8;
	if (scale < 8)
		scale = 8;
	if (scale > 12)
		scale = 12;


	kern = KERN_NONE;
	i = 0;
	while (string[i])
	{
		letter = (SLetter *)&LineFont[4*4];
		letter += string[i] - ' ';

		w = posz[i] * scale;
		w -= (width * scale) / 2;
		w += (widths[i] * scale) / 2;
w += scale / 2;
		angle = w * width / 63;
//angle = 0;

		x = (w * (s32)sin_tbl[(angle + 512) & 2047]) >> 14;
		y = (w * (s32)sin_tbl[angle & 2047]) >> 14;

		DrawLetter( letter, x+(32*8), y+h, angle*2, scale );

		kern = letter->KernRight;
		i++;
	}
}


/*******
static u32 GetUniform( u32 team )
{
	if (team > TEAM_SSN)
	{
		if (team >= TEAM_LFP && team <= TEAM_NOA)
			team -= 2;
		else	// newly created guys 
			team = TEAM_LFP-2;	//TEAM_LAL;
	}
	else if(team < 1)
	{	// this covers free agents (All-stars who have been released 
		// & created players not on a team(Edit player in shell)
		team = TEAM_LFP-2;	//TEAM_LAL;
	}
	return( team-1 );
}
*****/
static u32 GetUniform(	playert	*plyr)
{
int		team,team1;
	
	if(plyr->teamROM == NULL)
	{		// will be for newly created players
		team1 = plyr->plyrROM->ub___pTeam; 
	}
	else
	{
		team1 = plyr->teamROM->ub___tTeam; 
	}

	if(team1 == TEAM_EAS || team1 == TEAM_WES )	//|| team1 == TEAM_3PT)
	{
		// can't use FindPlayerOriginalTeam(int pnum) as apuw_gRoster[SOURCE_NBA] is NULL!!!!
		team = PlayerOriginalTeam[plyr->plyrROM->uw___pIndx & PLAYER_INDEX_MASK];
	}
	else
	{
		team = plyr->plyrROM->ub___pTeam; 
	}

	if (team > TEAM_SSN)
	{
		if (team >= TEAM_LFP && team <= TEAM_NOA)
			team -= 2;
		else	// newly created guys 
			team = TEAM_LFP-2;	//TEAM_LAL;
	}
	else if(team < 1)
	{	// this covers free agents (All-stars who have been released 
		// & created players not on a team(Edit player in shell)
		team = TEAM_LFP-2;	//TEAM_LAL;
	}
	return( team-1 );
}




void GetProperName( s32 pnum, char *name )
{
	TeamRamData_T
		*teamram;
	PlyrRomData_T
		*thisrom,
		*otherrom;
	s32
		i,
		team,
		same = FALSE;

	team = pnum < 5 ? 0 : 1;
	teamram = pcl__gMatch->apcl_mTeamRam[team];
	thisrom = player[pnum].plyrROM;

	if (!C3PT && teamram)
	{
		for (i=0; i<teamram->sb___tPlayers; i++)
		{
			otherrom = teamram->pcl__tPlyrRam[i].pcl__pPlyrRom;

			if (otherrom && otherrom != thisrom)
			{
				if (!stricmp( otherrom->pcz__pSName, thisrom->pcz__pSName ))
					same = TRUE;
			}
		}
	}

	if (same)
	{
		StrInitials( name, thisrom->pcz__pCName );
		strcat( name, " " );
		strcat( name, thisrom->pcz__pSName );
	}
	else
		strcpy( name, thisrom->pcz__pSName );

	strupper( name );
}


static void altertex( s32 pnum )
{
	SPlayerInfo
		*pi;
	u8
		*tex;
	char
		name[64];

	LineFont = AllocAndLoadFile( "BackName.LF", MEM_TMP );

	pi = &PlayerInfos[pnum];

	Big = (u8 *)dfb + (64*(32+32));
	Overlay = (u8 *)dfb;


	setcolor( pi->Palette[158].r,
						pi->Palette[158].g,
						pi->Palette[158].b );

	memset( Big, 0, (64*(32+32))*(4*4) );

	GetProperName( pnum, name );

	if (HTST)
		strcpy(name,HSDN);

	Font_Draw( name );
	SampleDown();

	tex = pi->MtlLists[TEXSET_HIGH][MTL_HI_BACK].Texture;
	ApplyOverlay( Overlay, tex, pi->Palette );

	tex = pi->MtlLists[TEXSET_HIGH][MTL_HI_LOWBACK].Texture;
	ApplyOverlay( &Overlay[64*31], tex, pi->Palette );

	MemFree( LineFont );
}


/******************************************************************************/

static s32 GetDigitWidth( s32 digit, const u8 *font )
{
	const u8
		*s;
	u8
		c;
	s32
		x,
		y,
		w,
		h,
		off,
		empty,
		width = 0;

	if (digit >= 10)
		digit -= 10;

	w = font[0];
	h = font[1];

	s = &font[2 + (digit * w * h)];

	for (x=0; x<w; x++)
	{
		off = x;
		empty = TRUE;

		for (y=0; y<h; y++)
		{
			c = s[off];
			off += w;

			if (c)
				empty = FALSE;
		}

		if (!empty)
			width++;
	}

	return( width );
}


static void DrawFrontDigit( s32 digit, const u8 *font, const s32 xpos, s32 ypos, SPlayerInfo *pi )
{
	SMaterial
		*mtl;
	const u8
		*s;
	u8
		c,
		*d;
	s32
		x,
		y,
		w,
		h,
		off,
		width,
		empty;

	if (digit >= 10)
		digit -= 10;

	w = font[0];
	h = font[1];

	s = &font[2 + (digit * w * h)];


	x = 0;
	empty = TRUE;
	while (empty && x < w)
	{
		off = x;

		for (y=0; y<h; y++)
		{
			c = s[off];
			off += w;

			if (c)
				empty = FALSE;
		}

		if (empty)
			x++;
	}

	off = x;
	width = GetDigitWidth( digit, font );
	y = 0;


	if (ypos < 0)
	{
		mtl = &pi->MtlLists[TEXSET_HIGH][MTL_HI_CHEST];			// hchest.bmp
		d = mtl->Texture;
		d += (mtl->Width * (mtl->Height + ypos)) + xpos;

		while (ypos < 0 && y < h)
		{
			for (x=0; x<width; x++)
			{
				c = s[off+x];

				if (c)
{
	if (ypos < mtl->Height && (x + xpos) < mtl->Width)
					d[x] = c;
}
			}

			off += w;
			d += mtl->Width;
			ypos++;
			y++;
		}
	}


	if (y < h)
	{
		mtl = &pi->MtlLists[TEXSET_HIGH][MTL_HI_ABDOMEN];			// habdomen.bmp
		d = mtl->Texture;
		d += (mtl->Width * ypos) + xpos;

		while (ypos < mtl->Height && y < h)
		{
			for (x=0; x<width; x++)
			{
				c = s[off+x];

				if (c)
					d[x] = c;
			}

			off += w;
			d += mtl->Width;
			ypos++;
			y++;
		}
	}
}


static void DrawFrontNums( const s32 pnum )
{
	static s8
		offsets[][2] =
		{
			15, 12,	// Atlanta Hawks ATL HAW						(4 x 8 )
			16, 12,	// Boston Celtics BOS CEL						(8 x 16)
			15, 10,	// Charlotte Hornets CHA HOR				(8 x 16)
			16, 12,	// Chicago Bulls CHI BUL						(8 x 16)
			15, 12,	// Cleveland Cavaliers CLE CAV			(8 x 10)
			16, 7,	// Dallas Mavericks DAL MAV					(8 x 16)
			16, 10,	// Denver Nuggets DEN NUG						(8 x 16)
			19, 12,	// Detroit Pistons DET PIS					(6 x 12)
			19, 8,	// Golden State Warriors GOL WAR		(8 x 16)
			8, -7,	// Houston Rockets HOU ROC					(4 x 8 )
			16, 8,	// Indiana Pacers IND PAC						(6 x 12)
			23, 4,	// Los Angeles Clippers LAC CLI			(6 x 12)
			16, 11,	// Los Angeles Lakers LAL LAK				(8 x 16)
			19, 16,	// Miami Heat MIA HEA								(8 x 16)
			16, 12,	// Milwaukee Bucks MIL BUC					(8 x 16)
			16, 9,	// Minnesota Timberwolves MIN WOL		(8 x 16)
			16, 9,	// New Jersey Nets NJ NET						(8 x 16)
			16, 6,	// New York Knicks NY KNI						(8 x 16)
			16, 10,	// Orlando Magic ORL MAG						(8 x 16)
			16, 11,	// Philadelphia 76'ers PHI SIX			(8 x 16)
			18, 9,	// Phoenix Suns PHO SUN							(8 x 16)
			25, 6,	// Portland Blazers POR BLA					(4 x 8 )
			16, 12,	// Sacramento Kings SAC KIN					(8 x 16)
			16, 10,	// San Antonio Spurs SA SPU					(8 x 16)
			21, 14,	// Seattle Sonics SEA SON						(6 x 12)
			15, 17,	// Toronto Raptors TOR RAP					(4 x 8 )
			20, 16,	// Utah Jazz UTA JAZ								(7 x 9 )
			16, 8,	// Vancouver Grizzlies VAN GRI			(6 x 12)
			20, 5,	// Washington Wizards WAS WIZ				(8 x 14)

			16, 19,	// LeftField Lefties LEF LEF
			16, 13,	// Nintendo NIN NIN
			16, 18,	// Nintendo2 NOA NOA
		};
	FH
		file;
	playert
		*plyr;
	SPlayerInfo
		*pi;
	u8
		*font;
	s32
		x,
		y,
		pos,
		size,
		team,
		number;

	plyr = &player[pnum];
	pi = &PlayerInfos[pnum];
	number = plyr->plyrROM->ub___pNum;
	team = GetUniform(plyr);


	file = FileOpen( "FrntNums.DIR", "rb" );
	FileSeek( file, 4 + (team * 2*4), SEEK_SET );

	FileRead( &pos, 4, 1, file );
	FileRead( &size, 4, 1, file );

	// Skip header...
	pos += (4*4);
	size -= (4*4);

	FileSeek( file, pos, SEEK_SET );

	font = MemMalloc( size, MEM_TMP );
	FileRead( font, size, 1, file );

	FileClose( file );


	x = offsets[team][0];
	y = offsets[team][1];
	size = font[0];

	if (number > 9)
	{
		size = 1;
		size += GetDigitWidth( number/10, font );
		size += GetDigitWidth( number%10, font );
		x -= (size/2);

		DrawFrontDigit( number/10, font, x, y, pi );
		x += GetDigitWidth( number/10, font ) + 1;
		DrawFrontDigit( number%10, font, x, y, pi );
	}
	else
	{
		size = GetDigitWidth( number, font );
		x -= (size/2);

		DrawFrontDigit( number, font, x, y, pi );
	}


	MemFree( font );
}


static void DrawBackDigit( s32 digit, const u8 *font, const s32 xpos, SPlayerInfo *pi )
{
	const u8
		*s;
	u8
		c,
		*d;
	s32
		x,
		y,
		w,
		h;

	if (digit >= 10)
		digit -= 10;

	w = font[0];
	h = font[1];

	s = &font[2 + (digit * w * h)];

	d = pi->MtlLists[TEXSET_HIGH][MTL_HI_LOWBACK].Texture;			// hlowback.bmp
	d += (8*64) + xpos;

	for (y=0; y<24; y++)
	{
		for (x=0; x<w; x++)
		{
			c = *s++;

			if (c)
				*d = c;

			d++;
		}

		d += (64-w);
	}

	d = pi->MtlLists[TEXSET_HIGH][MTL_HI_WAIST].Texture;			// hwaist.bmp
	d += xpos;

	for (y=0; y<(h-24); y++)
	{
		for (x=0; x<w; x++)
		{
			c = *s++;

			if (c)
				*d = c;

			d++;
		}

		d += (64-w);
	}
}


static void DrawBackNums( const s32 pnum )
{
	FH
		file;
	playert
		*plyr;
	SPlayerInfo
		*pi;
	u8
		*font;
	s32
		pos,
		size,
		team,
		number;

	plyr = &player[pnum];
	pi = &PlayerInfos[pnum];
	number = plyr->plyrROM->ub___pNum;
	team = GetUniform( plyr);


	file = FileOpen( "BackNums.DIR", "rb" );
	FileSeek( file, 4 + (team * 2*4), SEEK_SET );

	FileRead( &pos, 4, 1, file );
	FileRead( &size, 4, 1, file );

	// Skip header...
	pos += (4*4);
	size -= (4*4);

	FileSeek( file, pos, SEEK_SET );

	font = MemMalloc( size, MEM_TMP );
	FileRead( font, size, 1, file );

	FileClose( file );


	if (number > 9)
		DrawBackDigit( number/10, font, 15, pi );

	if (number < 10)
		DrawBackDigit( number, font, 24, pi );
	else
		DrawBackDigit( number%10, font, 33, pi );


	MemFree( font );
}


void FixUpMatList( void *m, s32 count )
{
	SMaterial
		*mat;
	u32
		base;

	mat = (SMaterial *)m;
	base = (u32)mat;

	while (count)
	{
		if (mat->Texture)
			mat->Texture += base;

		if (mat->Palette)
			mat->Palette += base/2;

		mat++;
		count--;
	}
}


/******************************************************************************/

vector16
	RackPosz[10] =
	{
		-6912, 1536, 12656, 0,
		-3632, 11008, 5984, 0,
		192, 16384, 5072, 0,
		4672, 21760, 6608, 0,
		6912, 33280, 14000, 0,

		+6912, 1536, -12656, 0,
		+3632, 11008, -5984, 0,
		-192, 16384, -5072, 0,
		-4672, 21760, -6608, 0,
		-6912, 33280, -14000, 0
	};


static u8
	*RackObj,
	*RackMtls;

static Gfx
	*RackList;

static Mtx
	RackMtxs[10];


static void Racks_Init( void *workspace )
{
	matrix
		mtx;
	Gfx
		*gp;
	s32
		i,
		size;

	if (!C3PT)
		return;


	RackObj = AllocAndLoadFile( "Rack.OBJ", MEM_ANY );
	RackMtls = AllocAndLoadFile( "Rack.MTL", MEM_ANY );
	FixUpMatList( RackMtls, 6 );


	gDPSetCombineMode( &ColModeCombine, G_CC_SHADE, G_CC_SHADE );
	gDPSetCombineMode( &TexModeCombine, G_CC_DECALRGBA, G_CC_DECALRGBA );

	gp = DrawObject( (Gfx *)workspace, RackObj, RackMtls );
	gSPEndDisplayList( gp++ );
	size = (s32)gp - (s32)workspace;
	RackList = MemMalloc( size, MEM_ANY );
	gp = DrawObject( RackList, RackObj, RackMtls );
	gSPEndDisplayList( gp++ );


	for (i=0; i<10; i++)
	{
		MakeMatPRH( &mtx, 0, 0, RackPosz[i].y );
		MatToN64( &mtx, &RackMtxs[i] );
		SetTransN64( &RackMtxs[i], RackPosz[i].x, 0, RackPosz[i].z );
	}
}


static void Racks_Deinit()
{
	if (!C3PT)
		return;

	MemFree( RackList );
	MemFree( RackMtls );
	MemFree( RackObj );
}


static Gfx *Racks_Draw( Gfx *gp )
{
	vector16
		vec;
	s32
		i;

	if (C3PT)
	{
		gDPPipeSync( gp++ );
		gDPSetRenderMode( gp++, G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2 );
		gSPClearGeometryMode( gp++, G_CULL_BACK );

		for (i=0; i<10; i++)
		{
			vec.x = RackPosz[i].x;
			vec.y = 0;
			vec.z = RackPosz[i].z;
			if (View_Inside( &vec, 1000 ))
			{
				gSPMatrix( gp++, &RackMtxs[i], G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );
				gSPDisplayList( gp++, RackList );
			}
		}

		gSPSetGeometryMode( gp++, G_CULL_BACK );
		gDPPipeSync( gp++ );
	}

	return( gp );
}


/******************************************************************************/

static void CombineExtremeFace( SPlayerInfo *pi )
{
	static u8
		*ear_mask[] =
		{
			"000001110000",
			"000111111100",
			"001111111110",
			"011111111110",
			"011111111110",
			"111111111111",
			"111111111111",
			"111111111111",
			"111111111111",
			"111111111111",
			"111111111111",
			"111111111111",
			"111111111111",
			"111111111111",
			"111111111111",
			"111111111111",
		};
	u8
		*src,
		*dst;
	RGBA32
		*rgb;
	RGBA16
		*pal;
	u32
		c,
		r,
		g,
		b;
	s32
		i,
		j,
		x,
		y;

	pal = pi->MtlLists[TEXSET_EXTREME][0].Palette;


	// front...

	src = pi->MtlLists[TEXSET_EXTREME][MTL_EX_HFRONT].Texture;
	rgb = (RGBA32 *)dfb;
	rgb += 48;

	for (y=0; y<64; y++)
	{
		for (x=0; x<32; x++)
		{
			c = *src++;

			rgb->r = pal[c].r;
			rgb->g = pal[c].g;
			rgb->b = pal[c].b;

			rgb++;
		}

		rgb += (256 - 32);
	}


	// side...

	src = pi->MtlLists[TEXSET_EXTREME][MTL_EX_HSIDE].Texture;
	rgb = (RGBA32 *)dfb;

	for (y=0; y<64; y++)
	{
		for (x=0; x<16; x++)
		{
			rgb[32+x].r = pal[src[x]].r;
			rgb[32+x].g = pal[src[x]].g;
			rgb[32+x].b = pal[src[x]].b;

			rgb[95-x].r = pal[src[x]].r;
			rgb[95-x].g = pal[src[x]].g;
			rgb[95-x].b = pal[src[x]].b;
		}

		src += 16;
		rgb += 256;
	}


	// Goatie...

	src = pi->MtlLists[TEXSET_HIGH][MTL_HI_HFACE].Texture;
	rgb = (RGBA32 *)dfb;

	for (y=0; y<64; y++)
	{
		for (x=0; x<16; x++)
		{
			c = *src++;

			if (c)
			{
				rgb[32+x].r = pal[c].r;
				rgb[32+x].g = pal[c].g;
				rgb[32+x].b = pal[c].b;

				rgb[95-x].r = pal[c].r;
				rgb[95-x].g = pal[c].g;
				rgb[95-x].b = pal[c].b;
			}
		}

		rgb += 256;
	}


	// back...

	src = pi->MtlLists[TEXSET_EXTREME][MTL_EX_HBACK].Texture;
	rgb = (RGBA32 *)dfb;

	for (y=0; y<64; y++)
	{
		for (x=0; x<16; x++)
		{
			rgb[(x*2)+0].r = pal[src[x]].r;
			rgb[(x*2)+0].g = pal[src[x]].g;
			rgb[(x*2)+0].b = pal[src[x]].b;
			rgb[(x*2)+1].r = pal[src[x]].r;
			rgb[(x*2)+1].g = pal[src[x]].g;
			rgb[(x*2)+1].b = pal[src[x]].b;

			rgb[126-(x*2)+0].r = pal[src[x]].r;
			rgb[126-(x*2)+0].g = pal[src[x]].g;
			rgb[126-(x*2)+0].b = pal[src[x]].b;
			rgb[126-(x*2)+1].r = pal[src[x]].r;
			rgb[126-(x*2)+1].g = pal[src[x]].g;
			rgb[126-(x*2)+1].b = pal[src[x]].b;
		}

		src += 16;
		rgb += 256;
	}

	// ears...

	src = pi->MtlLists[TEXSET_EXTREME][MTL_EX_HTOP].Texture;
	src += (32 * 32) + 20;
	rgb = (RGBA32 *)dfb;
	rgb += (28 * 256);

	for (y=0; y<16; y++)
	{
		for (x=0; x<12; x++)
		{
			if (ear_mask[y][x] == '1')
			{
				rgb[31 - x].r = pal[src[x]].r;
				rgb[31 - x].g = pal[src[x]].g;
				rgb[31 - x].b = pal[src[x]].b;

				rgb[96 + x].r = pal[src[x]].r;
				rgb[96 + x].g = pal[src[x]].g;
				rgb[96 + x].b = pal[src[x]].b;
			}
		}

		src += 32;
		rgb += 256;
	}


	// top...

	pal = pi->MtlLists[TEXSET_EXTREME][0].Palette;

	src = pi->MtlLists[TEXSET_EXTREME][MTL_EX_HTOP].Texture;
	rgb = (RGBA32 *)dfb;
	rgb += 128;

	for (y=0; y<32; y++)
	{
		for (x=0; x<32; x++)
		{
			rgb[x].r = pal[src[x]].r;
			rgb[x].g = pal[src[x]].g;
			rgb[x].b = pal[src[x]].b;
		}

		src += 32;
		rgb += 256;
	}
}


static void FindOthers( const RGBA16 color, const u8 index, RGBA16 *ptr, u8 *tex, s32 count )
{
	while (count > 0)
	{
		if (ptr->r == color.r &&
				ptr->g == color.g &&
				ptr->b == color.b &&
				ptr->a == 0)
		{
			*tex = index;
			ptr->a = 1;
		}

		ptr++;
		tex++;
		count--;
	}
}


static void FindColors( RGBA16 *rgb, u8 *tex, u32 size, RGBA16 *pal, u32 start, u32 count )
{
	RGBA16
		color;
	u32
		r,
		g,
		b,
		c;

	pal += start;

	while (size)
	{
		color = *rgb++;
		size--;

		if (color.a == 0)
		{
			r = color.r;
			g = color.g;
			b = color.b;

			c = Pal_FindColor( r, g, b, pal, count ) + start;

			*tex++ = c;

			FindOthers( color, c, rgb, tex, size );
		}
		else
		{
			tex++;
		}
	}
}


static void ShrinkFace( SPlayerInfo *pi )
{
	RGBA16
		*pal,
		*rgb;
	RGBA32
		*src;
	u8
		*dst;
	u32
		r,
		g,
		b;
	s32
		i,
		j,
		x,
		y;

	pal = pi->MtlLists[TEXSET_HIGH][0].Palette;


	// Face...

	// High...

	src = (RGBA32 *)dfb;
	rgb = (RGBA16 *)((u32)dfb + (256*128*4));

	for (y=0; y<32; y++)
	{
		for (x=0; x<64; x++)
		{
			r = 0;
			g = 0;
			b = 0;

			r += src[0].r;
			g += src[0].g;
			b += src[0].b;

			r += src[1].r;
			g += src[1].g;
			b += src[1].b;

			r += src[256].r;
			g += src[256].g;
			b += src[256].b;

			r += src[257].r;
			g += src[257].g;
			b += src[257].b;

			rgb->r = r >> 2;
			rgb->g = g >> 2;
			rgb->b = b >> 2;
			rgb->a = 0;

			rgb++;
			src += 2;
		}

		src += (2*256)-128;
	}


	rgb = (RGBA16 *)((u32)dfb + (256*128*4));
	dst = pi->MtlLists[TEXSET_HIGH][MTL_HI_HFACE].Texture;

	FindColors( rgb, dst, 64*32, pal, 29, 115 );


	// Low...

	src = (RGBA32 *)dfb;
	dst = pi->MtlLists[TEXSET_LOW][MTL_LO_HFACE].Texture;
	rgb = (RGBA16 *)((u32)dfb + (256*128*4));

	for (y=0; y<16; y++)
	{
		for (x=0; x<32; x++)
		{
			r = 0;
			g = 0;
			b = 0;

			for (j=0; j<4; j++)
			{
				for (i=0; i<4; i++)
				{
					r += src[(j*256) + (x*4) + i].r;
					g += src[(j*256) + (x*4) + i].g;
					b += src[(j*256) + (x*4) + i].b;
				}
			}

			rgb->r = r >> 4;
			rgb->g = g >> 4;
			rgb->b = b >> 4;
			rgb->a = 0;

			rgb++;
		}

		src += (4*256);
	}


	rgb = (RGBA16 *)((u32)dfb + (256*128*4));
	dst = pi->MtlLists[TEXSET_LOW][MTL_LO_HFACE].Texture;

	FindColors( rgb, dst, 32*16, pal, 29, 115 );



	// Top...

	// High...

	src = (RGBA32 *)dfb;
	src += 128;
	rgb = (RGBA16 *)((u32)dfb + (256*128*4));

	for (y=0; y<8; y++)
	{
		for (x=0; x<8; x++)
		{
			r = 0;
			g = 0;
			b = 0;

			for (j=0; j<4; j++)
			{
				for (i=0; i<4; i++)
				{
					r += src[(j*256) + (x*4) + i].r;
					g += src[(j*256) + (x*4) + i].g;
					b += src[(j*256) + (x*4) + i].b;
				}
			}

			rgb->r = r >> 4;
			rgb->g = g >> 4;
			rgb->b = b >> 4;
			rgb->a = 0;

			rgb++;
		}

		src += (4*256);
	}


	rgb = (RGBA16 *)((u32)dfb + (256*128*4));
	dst = pi->MtlLists[TEXSET_HIGH][MTL_HI_HTOP].Texture;

	FindColors( rgb, dst, 8*8, pal, 29, 115 );


	// Low...

	Copy64s( pi->MtlLists[TEXSET_HIGH][MTL_HI_HTOP].Texture,
					 pi->MtlLists[TEXSET_LOW ][MTL_LO_HTOP].Texture,
					 8 );
}


static void BuildCustomFace( playert *plyr, SPlayerInfo *pi )
{
	FH
		file;
	SFaceMake
		*facemake;
	RGBA32
		*src,
		*dst;
	RGBA16
		*pal;
	u8
		*top;
	u32
		a,
		r,
		g,
		b;
	s32
		x,
		y,
		head,
		hair;

	CombineExtremeFace( pi );


	x = plyr->plyrROM->uw___pIndx;
	head = GetCustomPlayerHead( x );
	hair = GetCustomPlayerHair( x );


	facemake = MemMalloc( sizeof( SFaceMake ), MEM_TMP );

	file = FileOpen( "FaceMake.DAT", "rb" );

	// Hi skins...
	FileSeek( file, (head * 92*2), SEEK_SET );
	FileRead( &pi->Palette[29], 92*2, 1, file );

	FileSeek( file, (20*92*2) + (hair * sizeof( SFaceMake )), SEEK_SET );
	FileRead( facemake, sizeof( SFaceMake ), 1, file );

	FileClose( file );


	src = facemake->HairTex;
	dst = (RGBA32 *)dfb;

	for (y=0; y<64; y++)
	{
		for (x=0; x<128; x++)
		{
			a = src->a;

			if (a)
			{
				r = ((u32)dst->r * a) >> 8;
				g = ((u32)dst->g * a) >> 8;
				b = ((u32)dst->b * a) >> 8;
			}
			else
			{
				r = 0;
				g = 0;
				b = 0;
			}

			dst->a = a;

			dst->r = r + (src->r >> 3);
			dst->g = g + (src->g >> 3);
			dst->b = b + (src->b >> 3);

			src++;
			dst++;
		}

		dst += (256-128);
	}

	top = facemake->TopTex;
	pal = facemake->TopPal;
	dst = (RGBA32 *)dfb;
	dst += 128;

	// Check for baldie...

	a = 0;
	for (x=0; x<(32*32); x++)
		a |= top[x];

	if (a)			// Not bald...
	{
		for (y=0; y<32; y++)
		{
			for (x=0; x<32; x++)
			{
				a = *top++;

				dst->r = pal[a].r;
				dst->g = pal[a].g;
				dst->b = pal[a].b;

				dst++;
			}

			dst += (256-32);
		}
	}

	Copy16s( facemake->ExPal, &pi->MtlLists[TEXSET_EXTREME][0].Palette[206], 50 );
	Copy16s( facemake->HiPal, &pi->Palette[29+92], 23 );

	MemFree( facemake );


	// Front...

	src = (RGBA32 *)dfb;
	src += 48;

	for (y=0; y<64; y++)
	{
		for (x=0; x<32; x++)
		{
			if (src->a != 255)
			{
				pi->MtlLists[TEXSET_EXTREME][MTL_EX_HFRONT].Texture[(y*32)+x] =
					Pal_FindColor( src->r, src->g, src->b, pi->MtlLists[TEXSET_EXTREME][0].Palette, 256 );
			}

			src++;
		}

		src += (256-32);
	}


	// Side...

	src = (RGBA32 *)dfb;
	src += 32;

	for (y=0; y<64; y++)
	{
		for (x=0; x<16; x++)
		{
			if (src->a != 255)
			{
				pi->MtlLists[TEXSET_EXTREME][MTL_EX_HSIDE].Texture[(y*16)+x] =
					Pal_FindColor( src->r, src->g, src->b, pi->MtlLists[TEXSET_EXTREME][0].Palette, 256 );
			}

			src++;
		}

		src += (256-16);
	}


	// Back...

	src = (RGBA32 *)dfb;

	for (y=0; y<64; y++)
	{
		for (x=0; x<16; x++)
		{
			if (src->a != 255)
			{
				pi->MtlLists[TEXSET_EXTREME][MTL_EX_HBACK].Texture[(y*16)+x] =
					Pal_FindColor( src->r, src->g, src->b, pi->MtlLists[TEXSET_EXTREME][0].Palette, 256 );
			}

			src += 2;
		}

		src += (256-32);
	}


	// Top...

	src = (RGBA32 *)dfb;
	src += 128;

	for (y=0; y<32; y++)
	{
		for (x=0; x<32; x++)
		{
			pi->MtlLists[TEXSET_EXTREME][MTL_EX_HTOP].Texture[(y*32)+x] =
				Pal_FindColor( src->r, src->g, src->b, pi->MtlLists[TEXSET_EXTREME][0].Palette, 256 );

			src++;
		}

		src += (256-32);
	}


	if (!ShellRedo)
		ShrinkFace( pi );
}


static u8 *GetNode( u8 *model, s32 type, s32 index )
{
	u8
		*ptr;
	s32
		offset;

	offset = *(s32 *)&model[5*4];
	ptr = &model[offset];

	while (ptr[0] != type)
		ptr += 16;
	
	offset = *(s32 *)&ptr[12];
	ptr = &model[offset];

	offset = *(s32 *)&ptr[index * 4];
	ptr = &model[offset];

	return( ptr );
}


static void GetMesh( const u8 *model, s32 type, s32 index, SMeshInfo *meshinfo )
{
	const u8
		*ptr;
	s32
		offset;

	offset = *(s32 *)&model[7*4];
	ptr = &model[offset];

	while (*(s16 *)ptr != type)
		ptr += 8;

	offset = *(s32 *)&ptr[4];
	ptr = &model[offset];

	ptr += (index * 12);

	offset = *(s32 *)&ptr[0];
	meshinfo->TexCoords = (u32 *)&model[offset];

	offset = *(s32 *)&ptr[4];
	meshinfo->Commands = (u16 *)&model[offset];
}


#if 0

static void AdjustHeadVerts( const void *model, const u32 adjust, const s32 count )
{
	SMeshInfo
		meshinfo;
	u16
		cmd,
		cnt,
		*ptr,
		*cptr;
	s32
		i;

	for (i=0; i<count; i++)
	{
		GetMesh( model, 0, i, &meshinfo );
		cptr = meshinfo.Commands;

		cmd = -1;
		while (cmd != CMD_END)
		{
			cmd = cptr[0];
			cnt = cptr[1];
			cptr += 2;

			switch (cmd)
			{
				case CMD_VERTICES:
					while (cnt > 0)
					{
						*cptr++ += adjust;
						cnt--;
					}
					break;

				case CMD_FACES:
					cptr += (cnt * 3);
					break;
			}
		}
	}
}

#else

static void AdjustHeadVerts( const void *model, const u32 adjust )
{
	SMeshInfo
		meshinfo;
	u16
		cmd,
		cnt,
		*ptr,
		*cptr;

	GetMesh( model, 0, 0, &meshinfo );
	cptr = meshinfo.Commands;

	cmd = -1;
	while (cmd != CMD_END)
	{
		cmd = cptr[0];
		cnt = cptr[1];
		cptr += 2;

		switch (cmd)
		{
			case CMD_VERTICES:
				while (cnt > 0)
				{
					*cptr++ += adjust;
					cnt--;
				}
				break;

			case CMD_FACES:
				cptr += (cnt * 3);
				break;
		}
	}
}

#endif


static void AdjustExtremeModel()
{
	SMeshInfo
		meshinfo;
	u16
		cmd,
		cnt,
		*ptr,
		*cptr;
	s32
		body;

	for (body=0; body<3; body++)
	{
		GetMesh( ExtremeModel, MESH_BODY, body, &meshinfo );
		cptr = meshinfo.Commands;

		cmd = -1;
		while (cmd != CMD_END)
		{
			cmd = cptr[0];
			cnt = cptr[1];
			cptr += 2;

			switch (cmd)
			{
				case CMD_VERTICES:
					while (cnt > 0)
					{
						if (*cptr >= VertStarts[DETAIL_EXTREME][NODE_HEAD]+2 &&
								*cptr < VertStarts[DETAIL_EXTREME][NODE_LSHOULDER])
							*cptr += 73-2;
						cptr++;
						cnt--;
					}
					break;

				case CMD_FACES:
					cptr += (cnt * 3);
					break;
			}
		}
	}
}


static void ApplySkin( SMaterial *src, SMaterial *dst, s32 count )
{
	s32
		i;

	while (count)
	{
		if (src->Texture)
		{
			for (i=0; i<(src->Width * src->Height); i++)
			{
				if (src->Texture[i])
				{
					if (dst->Texture[i] < 144)
						dst->Texture[i] = src->Texture[i];
				}
			}
		}

		src++;
		dst++;
		count--;
	}
}


static void LoadBuffSkin( SPlayerInfo *pi )
{
	SMaterial
		*mtl;

	mtl = AllocAndLoadFile( "Lo_Buff.MTL", MEM_TMP );
	FixUpMatList( mtl, 10 );
	ApplySkin( mtl, &pi->MtlLists[TEXSET_LOW][2], 10 );
	MemFree( mtl );

	mtl = AllocAndLoadFile( "Hi_Buff.MTL", MEM_TMP );
	FixUpMatList( mtl, 19 );
	ApplySkin( mtl, &pi->MtlLists[TEXSET_HIGH][2], 19 );
	MemFree( mtl );
}


static void InitAccessories( playert *plyr, SPlayerInfo *pi )
{
	SAccessories
		*access;
	SMaterial
		*src,
		*dst,
		*lo_mtl,
		*hi_mtl;
	u8
		mask,
		flags,
		obicep = FALSE,
		oforearm = FALSE,
		oknee = FALSE,
		ocalf = FALSE;
	u32
		size;

	lo_mtl = &pi->MtlLists[TEXSET_LOW][0];
	lo_mtl[MTL_LO_OBICEP]		= lo_mtl[MTL_LO_BICEP];
	lo_mtl[MTL_LO_OFOREARM]	= lo_mtl[MTL_LO_FOREARM];
	lo_mtl[MTL_LO_OCALF]		= lo_mtl[MTL_LO_CALF];

	lo_mtl[MTL_LO_OBICEP].Texture		= NULL;
	lo_mtl[MTL_LO_OFOREARM].Texture	= NULL;
	lo_mtl[MTL_LO_OCALF].Texture		= NULL;


	hi_mtl = &pi->MtlLists[TEXSET_HIGH][0];
	hi_mtl[MTL_HI_OBICEP]		= hi_mtl[MTL_HI_BICEP];
	hi_mtl[MTL_HI_OFOREARM]	= hi_mtl[MTL_HI_FOREARM];
	hi_mtl[MTL_HI_OKNEE]		= hi_mtl[MTL_HI_KNEE];
	hi_mtl[MTL_HI_OCALF]		= hi_mtl[MTL_HI_CALF];

	hi_mtl[MTL_HI_OBICEP].Texture = NULL;
	hi_mtl[MTL_HI_OFOREARM].Texture = NULL;
	hi_mtl[MTL_HI_OKNEE].Texture = NULL;
	hi_mtl[MTL_HI_OCALF].Texture = NULL;


	flags = plyr->plyrROM->ub___pAcc0;

	access = AllocAndLoadFile( "PAccess.TEX", MEM_TMP );


	// Decide which 'Other' textures we need...

	// Arm pads...
	mask = (flags & PLYR_HAS_BAPAD);
	if (mask && (mask != PLYR_HAS_BAPAD))
	{
		obicep = TRUE;
		oforearm = TRUE;
	}

	// Wrist pads...
	mask = (flags & PLYR_HAS_BWPAD);
	if (mask && (mask != PLYR_HAS_BWPAD))
		oforearm = TRUE;

	// Knee pads...
	mask = (flags & PLYR_HAS_BKPAD);
	if (mask && (mask != PLYR_HAS_BKPAD))
	{
		oknee = TRUE;
		ocalf = TRUE;
	}


	// Allocate 'Other' textures...

	if (obicep)
	{
		src = &pi->MtlLists[TEXSET_LOW][MTL_LO_BICEP];
		dst = &pi->MtlLists[TEXSET_LOW][MTL_LO_OBICEP];
		size = (src->Width * src->Height);
		dst->Texture = MemMalloc( size, MEM_ANY );
		Copy64s( src->Texture, dst->Texture, size>>3 );

		src = &pi->MtlLists[TEXSET_HIGH][MTL_HI_BICEP];
		dst = &pi->MtlLists[TEXSET_HIGH][MTL_HI_OBICEP];
		size = (src->Width * src->Height);
		dst->Texture = MemMalloc( size, MEM_ANY );
		Copy64s( src->Texture, dst->Texture, size>>3 );
	}

	if (oforearm)
	{
		src = &pi->MtlLists[TEXSET_LOW][MTL_LO_FOREARM];
		dst = &pi->MtlLists[TEXSET_LOW][MTL_LO_OFOREARM];
		size = (src->Width * src->Height);
		dst->Texture = MemMalloc( size, MEM_ANY );
		Copy64s( src->Texture, dst->Texture, size>>3 );

		src = &pi->MtlLists[TEXSET_HIGH][MTL_HI_FOREARM];
		dst = &pi->MtlLists[TEXSET_HIGH][MTL_HI_OFOREARM];
		size = (src->Width * src->Height);
		dst->Texture = MemMalloc( size, MEM_ANY );
		Copy64s( src->Texture, dst->Texture, size>>3 );
	}

	if (oknee)
	{
		src = &pi->MtlLists[TEXSET_HIGH][MTL_HI_KNEE];
		dst = &pi->MtlLists[TEXSET_HIGH][MTL_HI_OKNEE];
		size = (src->Width * src->Height);
		dst->Texture = MemMalloc( size, MEM_ANY );
		Copy64s( src->Texture, dst->Texture, size>>3 );
	}

	if (ocalf)
	{
		src = &pi->MtlLists[TEXSET_LOW][MTL_LO_CALF];
		dst = &pi->MtlLists[TEXSET_LOW][MTL_LO_OCALF];
		size = (src->Width * src->Height);
		dst->Texture = MemMalloc( size, MEM_ANY );
		Copy64s( src->Texture, dst->Texture, size>>3 );

		src = &pi->MtlLists[TEXSET_HIGH][MTL_HI_CALF];
		dst = &pi->MtlLists[TEXSET_HIGH][MTL_HI_OCALF];
		size = (src->Width * src->Height);
		dst->Texture = MemMalloc( size, MEM_ANY );
		Copy64s( src->Texture, dst->Texture, size>>3 );
	}


	lo_mtl = &pi->MtlLists[TEXSET_LOW][0];
	hi_mtl = &pi->MtlLists[TEXSET_HIGH][0];


	// Draw arm pads...

	if (flags & PLYR_HAS_LAPAD)
	{
		Copy64s( access->lArmBandT, &lo_mtl[MTL_LO_BICEP].Texture[12*16], 16*4/8 );
		Copy64s( access->lArmBandB, lo_mtl[MTL_LO_FOREARM].Texture, 16*3/8 );

		Copy64s( access->hArmBandT, &hi_mtl[MTL_HI_BICEP].Texture[24*32], 32*8/8 );
		Copy64s( access->hArmBandB, hi_mtl[MTL_HI_FOREARM].Texture, 32*6/8 );
	}

	if (flags & PLYR_HAS_RAPAD)
	{
		if (obicep)
		{
			Copy64s( access->lArmBandT, &lo_mtl[MTL_LO_OBICEP].Texture[12*16], 16*4/8 );
			Copy64s( access->hArmBandT, &hi_mtl[MTL_HI_OBICEP].Texture[24*32], 32*8/8 );
		}

		if (oforearm)
		{
			Copy64s( access->lArmBandB, lo_mtl[MTL_LO_OFOREARM].Texture, 16*3/8 );
			Copy64s( access->hArmBandB, hi_mtl[MTL_HI_OFOREARM].Texture, 32*6/8 );
		}
	}

	// Draw wrist pads...

	if (flags & PLYR_HAS_LWPAD)
	{
		Copy64s( access->lWristBand, &lo_mtl[MTL_LO_FOREARM].Texture[5*16], 16*2/8 );

		Copy64s( access->hWristBand, &hi_mtl[MTL_HI_FOREARM].Texture[9*32], 32*4/8 );
	}

	if (flags & PLYR_HAS_RWPAD && oforearm)
	{
		Copy64s( access->lWristBand, &lo_mtl[MTL_LO_OFOREARM].Texture[5*16], 16*2/8 );

		Copy64s( access->hWristBand, &hi_mtl[MTL_HI_OFOREARM].Texture[9*32], 32*4/8 );
	}

	// Draw knee pads...

	if (flags & PLYR_HAS_LKPAD)
	{
		Copy64s( access->lKneeBandT, &lo_mtl[MTL_LO_LTHIGH].Texture[11*16], 16*5/8 );
		Copy64s( access->lKneeBandB, lo_mtl[MTL_LO_CALF].Texture, 16*5/8 );

		Copy64s( access->hKneeBandT, &hi_mtl[MTL_HI_KNEE].Texture[5*32], 32*8/8 );
		Copy64s( access->hKneeBandB, hi_mtl[MTL_HI_CALF].Texture, 32*9/8 );
	}

	if (flags & PLYR_HAS_RKPAD)
		Copy64s( access->lKneeBandT, &lo_mtl[MTL_LO_RTHIGH].Texture[11*16], 16*5/8 );

	if (flags & PLYR_HAS_RKPAD)
	{
		if (oknee)
			Copy64s( access->hKneeBandT, &hi_mtl[MTL_HI_OKNEE].Texture[5*32], 32*8/8 );

		if (ocalf)
		{
			Copy64s( access->lKneeBandB, lo_mtl[MTL_LO_OCALF].Texture, 16*5/8 );
			Copy64s( access->hKneeBandB, hi_mtl[MTL_HI_OCALF].Texture, 32*9/8 );
		}
	}

	// Draw socks...

	if (flags & PLYR_HAS_SOCKS)
	{
		Copy64s( access->lSock, &lo_mtl[MTL_LO_CALF].Texture[5*16], 16*9/8 );
		Copy64s( access->hSock, &hi_mtl[MTL_HI_CALF].Texture[9*32], 32*19/8 );

		if (ocalf)
		{
			Copy64s( access->lSock, &lo_mtl[MTL_LO_OCALF].Texture[5*16], 16*9/8 );
			Copy64s( access->hSock, &hi_mtl[MTL_HI_OCALF].Texture[9*32], 32*19/8 );
		}
	}


	MemFree( access );


	src = &pi->MtlLists[TEXSET_HIGH][0];
	dst = &pi->MtlLists[TEXSET_EXTREME][0];

	dst[MTL_EX_OBICEP]		= src[MTL_HI_OBICEP];
	dst[MTL_EX_OFOREARM]	= src[MTL_HI_OFOREARM];
	dst[MTL_EX_OKNEE]			= src[MTL_HI_OKNEE];
	dst[MTL_EX_OCALF]			= src[MTL_HI_OCALF];
}


static void DeinitAccessories( SPlayerInfo *pi )
{
	SMaterial
		*mtl;
	s32
		i;

	mtl = &pi->MtlLists[TEXSET_HIGH][MTL_HI_OBICEP];
	for (i=0; i<4; i++)
	{
		if (mtl->Texture)
			MemFree( mtl->Texture );

		mtl++;
	}

	mtl = &pi->MtlLists[TEXSET_LOW][MTL_LO_OBICEP];
	for (i=0; i<3; i++)
	{
		if (mtl->Texture)
			MemFree( mtl->Texture );

		mtl++;
	}
}


static SPlayerInfo
	*Hi2Lo_pi;

static u32
	Hi2Lo_W,
	Hi2Lo_H;


static void Hi2Lo_Expand( const u32 mnum, const u32 pos )
{
	SMaterial
		*mtl;
	RGBA16
		*src;
	RGBA32
		*dst;
	u8
		*tex;
	s32
		x,
		y;

	mtl = &Hi2Lo_pi->MtlLists[TEXSET_HIGH][mnum];
	tex = mtl->Texture;
	dst = (RGBA32 *)dfb;
	dst += (pos * 64);

	for (y=0; y<mtl->Height; y++)
	{
		for (x=0; x<mtl->Width; x++)
		{
			src = &Hi2Lo_pi->Palette[*tex++];

			dst->r = src->r;
			dst->g = src->g;
			dst->b = src->b;

			dst++;
		}

		dst += (64 - mtl->Width);
	}
}


typedef struct SRect
{
	s32
		Left,
		Top,
		Right,
		Bottom;
} SRect;

static void Hi2Lo_Shrink( const u32 xpos, const u32 sw, const u32 sh, const u32 dw, const u32 dh )
{
	SRect
		srect,
		drect;
	RGBA16
		*rgb;
	RGBA32
		*src;
	u8
		*dst;
	s32
		rw,
		rh;
	u32
		r,
		g,
		b,
		i,
		j,
		x,
		y,
		k,
		muldiv;

	muldiv = (1 << 16) / (sw * sh);

	Hi2Lo_W = dw;
	Hi2Lo_H = dh;

	dst = (u8 *)dfb;
	dst += (64*128*4);

	rgb = (RGBA16 *)((u32)dfb + (64*128*4) + 64*32);

	for (y=0; y<dh; y++)
	{
		for (x=0; x<dw; x++)
		{
			r = 0;
			g = 0;
			b = 0;

			drect.Left = x * sw;
			drect.Right = drect.Left + sw;

			drect.Top = y * sh;
			drect.Bottom = drect.Top + sh;

			j = drect.Top / dh;
			do
			{
				i = drect.Left / dw;
				do
				{
					srect.Left = i * dw;
					srect.Right = srect.Left + dw;

					srect.Top = j * dh;
					srect.Bottom = srect.Top + dh;

					if (srect.Left < drect.Left)
						srect.Left = drect.Left;

					if (srect.Right > drect.Right)
						srect.Right = drect.Right;

					if (srect.Top < drect.Top)
						srect.Top = drect.Top;

					if (srect.Bottom > drect.Bottom)
						srect.Bottom = drect.Bottom;

					rw = srect.Right - srect.Left;
					rh = srect.Bottom - srect.Top;

					if (rw > 0 && rh > 0)
					{
						src = (RGBA32 *)dfb;
						src += (j * 64) + i + xpos;

						k = (rw * rh);
						r += src->r * k;
						g += src->g * k;
						b += src->b * k;
					}

					i++;
				} while (i < sw && rw);

				j++;
			} while (j < sh && rh);

			r = (r * muldiv) >> 16;
			g = (g * muldiv) >> 16;
			b = (b * muldiv) >> 16;

			rgb->r = r;
			rgb->g = g;
			rgb->b = b;
			rgb->a = 0;

			rgb++;
		}
	}


	rgb = (RGBA16 *)((u32)dfb + (64*128*4) + 64*32);

	FindColors( rgb, dst, dw*dh, Hi2Lo_pi->Palette, 0, 256 );
}


static void Hi2Lo_Paste( const u32 xpos, const u32 ypos )
{
	SMaterial
		*mtl;
	u8
		*src,
		*dst;
	u32
		x,
		y;

	mtl = &Hi2Lo_pi->MtlLists[TEXSET_LOW][MTL_LO_TORSO];

	src = (u8 *)dfb;
	src += (64*128*4);

	dst = mtl->Texture;
	dst += (ypos * mtl->Width) + xpos;

	for (y=0; y<Hi2Lo_H; y++)
	{
#if 1
		Copy8s( src, dst, Hi2Lo_W );
#else
		for (x=0; x<Hi2Lo_W; x++)
		{
			if (src[x])
				dst[x] = src[x];
		}
#endif

		src += Hi2Lo_W;
		dst += mtl->Width;
	}
}


static void Hi2Lo_Process( SPlayerInfo *pi )
{
	Hi2Lo_pi = pi;

	Hi2Lo_Expand( MTL_HI_ABDOMEN, 0 );
	Hi2Lo_Shrink( 0, 32, 38, 20, 20 );
	Hi2Lo_Paste( 22, 12 );

	Hi2Lo_Expand( MTL_HI_CHEST, 0 );
	Hi2Lo_Shrink( 0, 32, 16, 20, 12 );
	Hi2Lo_Paste( 22, 0 );

	Hi2Lo_Expand( MTL_HI_BACK, 0 );
	Hi2Lo_Expand( MTL_HI_LOWBACK, 32 );
	Hi2Lo_Expand( MTL_HI_WAIST, 64 );

	Hi2Lo_Shrink( 32, 32, 80, 10, 25 );
	Hi2Lo_Paste( 0, 0 );

	Hi2Lo_Shrink( 0, 32, 80, 10, 25 );
	Hi2Lo_Paste( 54, 0 );
}


static void LoadBodyTexs( playert *plyr, SPlayerInfo *pi )
{
	FH
		file;
	SUniform
		uniform;
	u8
		*tex;
	s32
		size,
		body,
		team,
		offset;

	file = FileOpen( "Uniforms.TEX", "rb" );

	FileRead( &uniform, sizeof( SUniform ), 1, file );

	size = (uniform.PalSize + uniform.LoSize + uniform.HiSize);

	team = GetUniform( plyr);

	offset = sizeof( SUniform ) + (team * 3 * size);
	FileSeek( file, offset, SEEK_SET );

	if (plyr->team != HOME_TEAM || C3PT)			// Away uniforms...
	{
		FileSeek( file, size, SEEK_CUR );

		if (!AttractMode && UseOtherUniform)
		{
			if (uniform.Others & (1 << team))
				FileSeek( file, size, SEEK_CUR );
		}
	}

	FileRead( &pi->Palette[144], uniform.PalSize, 1, file );

	tex = pi->MtlLists[TEXSET_LOW][MTL_LO_SHOULDR].Texture;
	FileRead( tex, uniform.LoSize, 1, file );

	tex = pi->MtlLists[TEXSET_HIGH][MTL_HI_ABDOMEN].Texture;
	FileRead( tex, uniform.HiSize, 1, file );

	FileClose( file );


	{
		SAccessories
			*access;
		u32
			pal;

		pal = (plyr->plyrROM->ub___pAcc1 & PLYR_PADS_MASK);

		if (pal != PLYR_PADS_TEAM)
		{
			access = AllocAndLoadFile( "PAccess.TEX", MEM_TMP );

			Copy16s( access->Palettes[pal-1], &pi->Palette[163], 13 );

			MemFree( access );
		}
	}


	DrawBackNums( plyr->no );
	DrawFrontNums( plyr->no );

	altertex( plyr->no );

#ifndef DEBUG
	if (!ShellRedo)
		Hi2Lo_Process( pi );
#endif


	body = (plyr->plyrROM->ub___pAcc1 >> 6) & 3;

	if (body == BODY_THICK)
		LoadBuffSkin( pi );
}


static void AllocPlayer( SPlayerInfo *pi )
{
	static u8
		highinfo[21][4] =
		{
			8, 8, 3, 3,
			64, 32, 6, 5,
			32, 38, 5, 6,
			32, 16, 5, 4,
			16, 32, 4, 5,
			16, 32, 4, 5,
			64, 32, 6, 5,
			64, 32, 6, 5,
			32, 16, 5, 4,
			32, 32, 5, 5,
			32, 16, 5, 4,
			32, 16, 5, 4,
			32, 16, 5, 4,
			64, 16, 6, 4,
			64, 32, 6, 5,
			8, 8, 3, 3,
			32, 22, 5, 5,
			32, 22, 5, 5,
			32, 13, 5, 4,
			32, 32, 5, 5,
			16, 16, 4, 4
		},
		lowinfo[12][4] =
		{
			8, 8, 3, 3,
			32, 16, 5, 4,
			16, 8, 4, 3,
			64, 32, 6, 5,
			16, 16, 4, 4,
			16, 8, 4, 3,
			16, 8, 4, 3,
			32, 8, 5, 3,
			16, 16, 4, 4,
			16, 16, 4, 4,
			16, 20, 4, 5,
			8, 8, 3, 3
		};
	SMaterial
		*mtl;
	u8
		*tex;
	s32
		i,
		size;

	pi->Palette = MemMalloc( 256*2, MEM_ANY );


	tex = MemMalloc( (15 + 25 + 27) * sizeof( SMaterial ), MEM_ANY );
	pi->MtlLists[TEXSET_LOW] = (SMaterial *)tex;
	tex += 15 * sizeof( SMaterial );
	pi->MtlLists[TEXSET_HIGH] = (SMaterial *)tex;
	tex += 25 * sizeof( SMaterial );
	pi->MtlLists[TEXSET_EXTREME] = (SMaterial *)tex;


	size = 0;
	for (i=0; i<12; i++)
		size += (s32)lowinfo[i][0] * (s32)lowinfo[i][1];

	tex = MemMalloc( size, MEM_ANY );

	for (i=0; i<12; i++)
	{
		mtl = &pi->MtlLists[TEXSET_LOW][i];

		mtl->Texture			= tex;
		mtl->Palette			= NULL;
		mtl->Width				= lowinfo[i][0];
		mtl->Height				= lowinfo[i][1];
		mtl->Flags.Xmask	= lowinfo[i][2];
		mtl->Flags.Ymask	= lowinfo[i][3];
		mtl->Flags.Xflags	= G_TX_WRAP;
		mtl->Flags.Yflags	= G_TX_CLAMP;
		mtl->Flags.Format	= FORMAT_CI8;

		if (i == 0)
			mtl->Palette = pi->Palette;

		tex += (s32)lowinfo[i][0] * (s32)lowinfo[i][1];
	}


	size = 0;
	for (i=0; i<21; i++)
		size += (s32)highinfo[i][0] * (s32)highinfo[i][1];

	tex = MemMalloc( size, MEM_ANY );

	for (i=0; i<21; i++)
	{
		mtl = &pi->MtlLists[TEXSET_HIGH][i];

		mtl->Texture			= tex;
		mtl->Palette			= NULL;
		mtl->Width				= highinfo[i][0];
		mtl->Height				= highinfo[i][1];
		mtl->Flags.Xmask	= highinfo[i][2];
		mtl->Flags.Ymask	= highinfo[i][3];
		mtl->Flags.Xflags	= G_TX_WRAP;
		mtl->Flags.Yflags	= G_TX_CLAMP;
		mtl->Flags.Format	= FORMAT_CI8;

		if (i == 0)
			mtl->Palette = pi->Palette;

		tex += (s32)highinfo[i][0] * (s32)highinfo[i][1];
	}


	Copy32s( ExtremeMtls, &pi->MtlLists[TEXSET_EXTREME][0], 4 * sizeof( SMaterial ) / 4 );
	Copy32s( &pi->MtlLists[TEXSET_HIGH][2], &pi->MtlLists[TEXSET_EXTREME][4], 19 * sizeof( SMaterial ) / 4 );

	mtl = &pi->MtlLists[TEXSET_EXTREME][4];
	mtl->Palette = pi->Palette;


	mtl = pi->MtlLists[TEXSET_EXTREME];

	mtl[MTL_EX_HTOP].Palette = MemMalloc( 256*2, MEM_ANY );

	size = 0;
	for (i=0; i<4; i++)
		size += (mtl[i].Width * mtl[i].Height);

	tex = MemMalloc( size, MEM_ANY );

	for (i=0; i<4; i++)
	{
		mtl[i].Texture = tex;
		tex += (mtl[i].Width * mtl[i].Height);
	}
}



static void DeallocPlayer( SPlayerInfo *pi )
{
	u8
		*tex;

	MemFree( pi->MtlLists[TEXSET_EXTREME][0].Texture );
	MemFree( pi->MtlLists[TEXSET_EXTREME][0].Palette );

	MemFree( pi->MtlLists[TEXSET_HIGH][0].Texture );
	MemFree( pi->MtlLists[TEXSET_LOW][0].Texture );
	MemFree( pi->MtlLists[TEXSET_LOW] );

	MemFree( pi->Palette );
}


static void LoadSkin( playert *plyr, SPlayerInfo *pi )
{
	static u8
		palnums[] =
		{
			0, 0,
			1, 1, 1,
			2, 2, 2,
			3, 3,
			4, 4,
			5, 5, 5,
			6, 6, 6,
			7, 7
		};
	PlyrCustomInfo_T
		*custom;
	RGBA16
		*skinpals;
	s32
		i;

	skinpals = AllocAndLoadFile( "Skins.PAL", MEM_TMP );

	i = plyr->plyrROM->uw___pIndx;
	if (i > si___gPlyrRomCnt)
	{

//		custom = (PlyrCustomInfo_T *)&plyr->plyrROM->cl___pStatNBA;
//		i = palnums[custom->ub___pCustomHeadType];
		i = palnums[GetCustomPlayerHead(i)];
		Copy16s( &skinpals[i * 28], &pi->Palette[1], 28 );
	}
	else
		Copy16s( &skinpals[(u32)plyr->plyrROM->ub___pSkin * 28], &pi->Palette[1], 28 );

	MemFree( skinpals );
}


static void LoadHead( playert *plyr, SPlayerInfo *pi )
{
	FH
		file;
	SMaterial
		*mtl;
	s32
		i,
		size,
		offset;

	file = FileOpen( "HEADS.DIR", "rb" );

	offset  = i = plyr->plyrROM->uw___pIndx;
	if (i > si___gPlyrRomCnt)
	{	// special stuff for custom players
		//PlyrCustomInfo_T	*custom;

//		custom = (PlyrCustomInfo_T *)&plyr->plyrROM->cl___pStatNBA;
//		i = si___gPlyrRomCnt + custom->ub___pCustomHeadType + 1;
		offset = i = si___gPlyrRomCnt + GetCustomPlayerHead(i) + 1;	// at end of list
	}

	i *= (2 * 4);
	FileSeek( file, 4+i+4, SEEK_SET );
	FileRead( &size, 4, 1, file );

	pi->HeadModel = MemMalloc( size, MEM_ANY );

	FileSeek( file, -(2*4), SEEK_CUR );
	FileRead( &i, 4, 1, file );
	FileSeek( file, i, SEEK_SET );

	FileRead( pi->HeadModel, size, 1, file );

	FileClose( file );


	AdjustHeadVerts( pi->HeadModel, VertStarts[DETAIL_EXTREME][NODE_HEAD] );
	GetMesh( pi->HeadModel, 0, 0, &pi->MeshLists[DETAIL_EXTREME][MESH_HEAD] );
	GetHeadBases( pi );

	for (i=0; i<HEAD_NODES; i++)
		pi->HeadNodes[i] = GetNode( pi->HeadModel, i, 0 );


	file = FileOpen( "Heads.GFX", "rb" );
/*****
	offset = plyr->plyrROM->uw___pIndx;
	if (offset > si___gPlyrRomCnt)
	{
//		PlyrCustomInfo_T	*custom;
//		custom = (PlyrCustomInfo_T *)&plyr->plyrROM->cl___pStatNBA;
//		offset = si___gPlyrRomCnt + custom->ub___pCustomHeadType + 1;

		offset = si___gPlyrRomCnt + GetCustomPlayerHead(i) + 1;	// at end of list

	}
******/
	offset *= sizeof( SHeadGfx );
	FileSeek( file, offset, SEEK_SET );

	mtl = pi->MtlLists[TEXSET_EXTREME];

	FileRead( &pi->Palette[29], 115*2, 1, file );
	FileSeek( file, 2, SEEK_CUR );

	mtl = &pi->MtlLists[TEXSET_EXTREME][MTL_EX_HTOP];
	FileRead( mtl->Palette, 256*2, 1, file );
	FileRead( mtl->Texture, mtl->Width, mtl->Height, file );

	mtl = &pi->MtlLists[TEXSET_EXTREME][MTL_EX_HFRONT];
	FileRead( mtl->Texture, mtl->Width, mtl->Height, file );

	mtl = &pi->MtlLists[TEXSET_EXTREME][MTL_EX_HBACK];
	FileRead( mtl->Texture, mtl->Width, mtl->Height, file );

	mtl = &pi->MtlLists[TEXSET_EXTREME][MTL_EX_HSIDE];
	FileRead( mtl->Texture, mtl->Width, mtl->Height, file );

	mtl = &pi->MtlLists[TEXSET_HIGH][MTL_HI_HFACE];			// Goatie fix.
	FileRead( mtl->Texture, 16, 64, file );

	FileClose( file );


	i = plyr->plyrROM->uw___pIndx;
	if (i > si___gPlyrRomCnt)
		BuildCustomFace( plyr, pi );
	else
	{
		CombineExtremeFace( pi );
		ShrinkFace( pi );
	}
}


static void FreeHead( SPlayerInfo *pi )
{
	MemFree( pi->HeadModel );
}


void InitPlayer( s32 pnum )
{
	FH
		file;
	playert
		*plyr;
	SPlayerInfo
		*pi;
	SMaterial
		*mtl;
	u8
		*tex;
	u16
		psize,
		lsize,
		hsize;
	u32
		team,
		offset;
	s32
		i,
		size;

	pi = &PlayerInfos[pnum];
	plyr = &player[pnum];

	if (!pi->Active)
		return;


	pi->FaceAnim = 0;
	pi->FaceFrame = 0;


	AllocPlayer( pi );


	LoadSkin( plyr, pi );
	LoadHead( plyr, pi );

	LoadBodyTexs( plyr, pi );


	{
		s32
			head,
			body;

		head = (plyr->plyrROM->ub___pAcc1 >> 3) & 7;
		body = (plyr->plyrROM->ub___pAcc1 >> 6) & 3;

//  error check
/**		i = plyr->plyrROM->uw___pIndx;
		if (i >= si___gPlyrRomCnt)
			head = 1;
*****/
		for (i=0; i<MAX_BONES; i++)
		{
			switch (i)
			{
				case NODE_HEAD:
					pi->NodeLists[DETAIL_LOW    ][i] = GetNode( LowModel,     i, body );
					pi->NodeLists[DETAIL_MEDIUM ][i] = GetNode( MediumModel,  i, body );
					pi->NodeLists[DETAIL_HIGH   ][i] = GetNode( HighModel,    i, (body*5)+head );
					pi->NodeLists[DETAIL_EXTREME][i] = NULL;
					break;

				case NODE_LHAND:
				case NODE_RHAND:
					pi->NodeLists[DETAIL_LOW    ][i] = GetNode( LowModel,     i, (body*2) );
					pi->NodeLists[DETAIL_MEDIUM ][i] = GetNode( MediumModel,  i, (body*2) );
					pi->NodeLists[DETAIL_HIGH   ][i] = GetNode( HighModel,    i, (body*2) );
					pi->NodeLists[DETAIL_EXTREME][i] = GetNode( ExtremeModel, i, (body*2) );
					break;

				default:
					pi->NodeLists[DETAIL_LOW    ][i] = GetNode( LowModel,     i, body );
					pi->NodeLists[DETAIL_MEDIUM ][i] = GetNode( MediumModel,  i, body );
					pi->NodeLists[DETAIL_HIGH   ][i] = GetNode( HighModel,    i, body );
					pi->NodeLists[DETAIL_EXTREME][i] = GetNode( ExtremeModel, i, body );
			}
		}


		GetMesh( LowModel, MESH_BODY,  body,     &pi->MeshLists[DETAIL_LOW][MESH_BODY] );
		GetMesh( LowModel, MESH_HEAD,  body,     &pi->MeshLists[DETAIL_LOW][MESH_HEAD] );
		GetMesh( LowModel, MESH_LHAND, (body*2), &pi->MeshLists[DETAIL_LOW][MESH_LHAND] );
		GetMesh( LowModel, MESH_RHAND, (body*2), &pi->MeshLists[DETAIL_LOW][MESH_RHAND] );

		GetMesh( MediumModel, MESH_BODY,  body,     &pi->MeshLists[DETAIL_MEDIUM][MESH_BODY] );
		GetMesh( MediumModel, MESH_HEAD,  body,     &pi->MeshLists[DETAIL_MEDIUM][MESH_HEAD] );
		GetMesh( MediumModel, MESH_LHAND, (body*2), &pi->MeshLists[DETAIL_MEDIUM][MESH_LHAND] );
		GetMesh( MediumModel, MESH_RHAND, (body*2), &pi->MeshLists[DETAIL_MEDIUM][MESH_RHAND] );

		GetMesh( HighModel, MESH_BODY,  body,          &pi->MeshLists[DETAIL_HIGH][MESH_BODY] );
		GetMesh( HighModel, MESH_HEAD,  (body*5)+head, &pi->MeshLists[DETAIL_HIGH][MESH_HEAD] );
		GetMesh( HighModel, MESH_LHAND, (body*2),      &pi->MeshLists[DETAIL_HIGH][MESH_LHAND] );
		GetMesh( HighModel, MESH_RHAND, (body*2),      &pi->MeshLists[DETAIL_HIGH][MESH_RHAND] );

		GetMesh( ExtremeModel, MESH_BODY,  body,     &pi->MeshLists[DETAIL_EXTREME][MESH_BODY] );
		GetMesh( ExtremeModel, MESH_LHAND, (body*2), &pi->MeshLists[DETAIL_EXTREME][MESH_LHAND] );
		GetMesh( ExtremeModel, MESH_RHAND, (body*2), &pi->MeshLists[DETAIL_EXTREME][MESH_RHAND] );


		for (i=0; i<3; i++)
		{
			// Low...

			LHandNodes[DETAIL_LOW][i][HAND_OPEN] = GetNode( LowModel, NODE_LHAND, (i*2)+HAND_OPEN );
			LHandNodes[DETAIL_LOW][i][HAND_FIST] = GetNode( LowModel, NODE_LHAND, (i*2)+HAND_FIST );

			RHandNodes[DETAIL_LOW][i][HAND_OPEN] = GetNode( LowModel, NODE_RHAND, (i*2)+HAND_OPEN );
			RHandNodes[DETAIL_LOW][i][HAND_FIST] = GetNode( LowModel, NODE_RHAND, (i*2)+HAND_FIST );


			GetMesh( LowModel, MESH_LHAND, (i*2)+HAND_OPEN, &LHandMeshes[DETAIL_LOW][i][HAND_OPEN] );
			GetMesh( LowModel, MESH_LHAND, (i*2)+HAND_FIST, &LHandMeshes[DETAIL_LOW][i][HAND_FIST] );

			GetMesh( LowModel, MESH_RHAND, (i*2)+HAND_OPEN, &RHandMeshes[DETAIL_LOW][i][HAND_OPEN] );
			GetMesh( LowModel, MESH_RHAND, (i*2)+HAND_FIST, &RHandMeshes[DETAIL_LOW][i][HAND_FIST] );


			// Medium...

			LHandNodes[DETAIL_MEDIUM][i][HAND_OPEN] = GetNode( MediumModel, NODE_LHAND, (i*2)+HAND_OPEN );
			LHandNodes[DETAIL_MEDIUM][i][HAND_FIST] = GetNode( MediumModel, NODE_LHAND, (i*2)+HAND_FIST );

			RHandNodes[DETAIL_MEDIUM][i][HAND_OPEN] = GetNode( MediumModel, NODE_RHAND, (i*2)+HAND_OPEN );
			RHandNodes[DETAIL_MEDIUM][i][HAND_FIST] = GetNode( MediumModel, NODE_RHAND, (i*2)+HAND_FIST );


			GetMesh( MediumModel, MESH_LHAND, (i*2)+HAND_OPEN, &LHandMeshes[DETAIL_MEDIUM][i][HAND_OPEN] );
			GetMesh( MediumModel, MESH_LHAND, (i*2)+HAND_FIST, &LHandMeshes[DETAIL_MEDIUM][i][HAND_FIST] );

			GetMesh( MediumModel, MESH_RHAND, (i*2)+HAND_OPEN, &RHandMeshes[DETAIL_MEDIUM][i][HAND_OPEN] );
			GetMesh( MediumModel, MESH_RHAND, (i*2)+HAND_FIST, &RHandMeshes[DETAIL_MEDIUM][i][HAND_FIST] );


			// High...

			LHandNodes[DETAIL_HIGH][i][HAND_OPEN] = GetNode( HighModel, NODE_LHAND, (i*2)+HAND_OPEN );
			LHandNodes[DETAIL_HIGH][i][HAND_FIST] = GetNode( HighModel, NODE_LHAND, (i*2)+HAND_FIST );

			RHandNodes[DETAIL_HIGH][i][HAND_OPEN] = GetNode( HighModel, NODE_RHAND, (i*2)+HAND_OPEN );
			RHandNodes[DETAIL_HIGH][i][HAND_FIST] = GetNode( HighModel, NODE_RHAND, (i*2)+HAND_FIST );


			GetMesh( HighModel, MESH_LHAND, (i*2)+HAND_OPEN, &LHandMeshes[DETAIL_HIGH][i][HAND_OPEN] );
			GetMesh( HighModel, MESH_LHAND, (i*2)+HAND_FIST, &LHandMeshes[DETAIL_HIGH][i][HAND_FIST] );

			GetMesh( HighModel, MESH_RHAND, (i*2)+HAND_OPEN, &RHandMeshes[DETAIL_HIGH][i][HAND_OPEN] );
			GetMesh( HighModel, MESH_RHAND, (i*2)+HAND_FIST, &RHandMeshes[DETAIL_HIGH][i][HAND_FIST] );


			// Extreme...

			LHandNodes[DETAIL_EXTREME][i][HAND_OPEN] = GetNode( ExtremeModel, NODE_LHAND, (i*2)+HAND_OPEN );
			LHandNodes[DETAIL_EXTREME][i][HAND_FIST] = GetNode( ExtremeModel, NODE_LHAND, (i*2)+HAND_FIST );

			RHandNodes[DETAIL_EXTREME][i][HAND_OPEN] = GetNode( ExtremeModel, NODE_RHAND, (i*2)+HAND_OPEN );
			RHandNodes[DETAIL_EXTREME][i][HAND_FIST] = GetNode( ExtremeModel, NODE_RHAND, (i*2)+HAND_FIST );


			GetMesh( ExtremeModel, MESH_LHAND, (i*2)+HAND_OPEN, &LHandMeshes[DETAIL_EXTREME][i][HAND_OPEN] );
			GetMesh( ExtremeModel, MESH_LHAND, (i*2)+HAND_FIST, &LHandMeshes[DETAIL_EXTREME][i][HAND_FIST] );

			GetMesh( ExtremeModel, MESH_RHAND, (i*2)+HAND_OPEN, &RHandMeshes[DETAIL_EXTREME][i][HAND_OPEN] );
			GetMesh( ExtremeModel, MESH_RHAND, (i*2)+HAND_FIST, &RHandMeshes[DETAIL_EXTREME][i][HAND_FIST] );
		}
	}


	InitAccessories( plyr, pi );

	ShellRedo = 0;
}


void DeinitPlayer( s32 pnum )
{
	SPlayerInfo
		*pi;
	u8
		*tex;

	pi = &PlayerInfos[pnum];

	if (!pi->Active)
		return;

	DeinitAccessories( pi );

	FreeHead( pi );

	DeallocPlayer( pi );
}


static void Clouds_Add( s32 x, s32 y, s32 z )
{
	vector16
		*v;
	s32
		i;

	for (i=(MAX_CLOUDS-1); i>0; i--)
		CloudList[i] = CloudList[i-1];

	v = &CloudList[0];

	v->x = x;
	v->y = y;
	v->z = z;
	v->w = 255;
}


static void Clouds_Update()
{
	vector16
		*v;
	s32
		i;

	if (ArcadeMode && !Paused)
	{
		v = CloudList;

		for (i=0; i<MAX_CLOUDS; i++)
		{
			if (v->w)
			{
				v->w -= 12;
				if (v->w < 0)
					v->w = 0;

				if (v->y > 0)
					v->y -= 16;
			}

			v++;
		}
	}
}


static Gfx *Clouds_Draw( Gfx *gp, s32 db, dyn *d )
{
	static Vtx
		verts[2][MAX_CLOUDS][4];/* =
		{
			-150, +150, 0, 0, ( 0<<6)+32, ( 0<<6)-32, 255, 255, 255, 255,
			+150, +150, 0, 0, (16<<6)+32, ( 0<<6)-32, 255, 255, 255, 255,
			+150, -150, 0, 0, (16<<6)+32, (16<<6)-32, 255, 255, 255, 255,
			-150, -150, 0, 0, ( 0<<6)+32, (16<<6)-32, 255, 255, 255, 255,
		};*/
	VertexN64
		*v;
	vector16
		*pos,
		vec;
	s32
		i;

	if (!ArcadeMode || replay_flag)
		return( gp );


	gDPPipeSync( gp++ );
	gSPTexture( gp++, 0x8000, 0x8000, 0, G_TX_RENDERTILE, G_ON );
	gDPSetCombineMode( gp++, G_CC_MODULATEIA, G_CC_MODULATEIA );
	gDPSetRenderMode( gp++, G_RM_ZB_CLD_SURF, G_RM_ZB_CLD_SURF2 );
	gDPSetTextureLUT( gp++, G_TT_NONE );
//	gSPMatrix( gp++, &d->identity, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );

	gDPLoadTextureBlock( gp++,
											 CloudTex,
											 G_IM_FMT_IA, G_IM_SIZ_8b,
											 16, 16,
											 0,
											 G_TX_CLAMP,
											 G_TX_CLAMP,
											 4, 4,
											 G_TX_NOLOD, G_TX_NOLOD );

	v = (VertexN64 *)&verts[db][0][0];
	pos = CloudList;

	for (i=0; i<MAX_CLOUDS; i++)
	{
		if (pos->w)
		{
			gSPVertex( gp++, v, 4, 0 );

			vec.x = -150;
			vec.y = +150;
			vec.z = 0;
			Vec16MulMat( &vec, &View->Cam2World, &vec );
			v->x = vec.x + pos->x;
			v->y = vec.y + pos->y;
			v->z = vec.z + pos->z;
			v->u = ( 0 << 6) + 32;
			v->v = ( 0 << 6) + 32;
			v->r = 255;
			v->g = 255;
			v->b = 255;
			v->a = pos->w;
			v++;

			vec.x = +150;
			vec.y = +150;
			vec.z = 0;
			Vec16MulMat( &vec, &View->Cam2World, &vec );
			v->x = vec.x + pos->x;
			v->y = vec.y + pos->y;
			v->z = vec.z + pos->z;
			v->u = (15 << 6) + 32;
			v->v = ( 0 << 6) + 32;
			v->r = 255;
			v->g = 255;
			v->b = 255;
			v->a = pos->w;
			v++;

			vec.x = +150;
			vec.y = -150;
			vec.z = 0;
			Vec16MulMat( &vec, &View->Cam2World, &vec );
			v->x = vec.x + pos->x;
			v->y = vec.y + pos->y;
			v->z = vec.z + pos->z;
			v->u = (15 << 6) + 32;
			v->v = (15 << 6) + 32;
			v->r = 255;
			v->g = 255;
			v->b = 255;
			v->a = pos->w;
			v++;

			vec.x = -150;
			vec.y = -150;
			vec.z = 0;
			Vec16MulMat( &vec, &View->Cam2World, &vec );
			v->x = vec.x + pos->x;
			v->y = vec.y + pos->y;
			v->z = vec.z + pos->z;
			v->u = ( 0 << 6) + 32;
			v->v = (15 << 6) + 32;
			v->r = 255;
			v->g = 255;
			v->b = 255;
			v->a = pos->w;
			v++;

			gSP2Triangles( gp++, 0, 2, 1, 0, 0, 3, 2, 0 );
		}
		else
		{
			v += 4;
		}

		pos++;
	}

	gDPPipeSync( gp++ );
	gDPSetTextureLUT( gp++, G_TT_RGBA16 );

	return( gp );
}


void Clouds_Save( s32 *buf )
{
	s32
		bits = 0;

	bits |= (ShowTrails & 1);
	bits |= (BallTrail & 1) << 1;

	*buf = bits;
}


void Clouds_Load( s32 *buf )
{
	s32
		bits;

	bits = *buf;

	BallTrail = (bits >> 1) & 1;
	ShowTrails = (bits & 1);
}


static void CheckPreNMI()
{
	if (PreNMI)
	{
		Audio_Deinit();
		osViBlack( TRUE );
		RestoreVideoModes();
		while (TRUE);
	}
}


s8
	DumpGfxLoaded;


void LoadDumpGfx()
{
	s32
		i;

	if (!DumpGfxLoaded)
	{
// not in here in case of 3 pt 
//		for(i=0; i<10; i++)		refresh_player(&player[i]);	// in case of substitution

		for (i=0; i<10; i++)
		{
			InitPlayer( i );
			CheckPreNMI();
		}

		
		VtxLists[0] = MemMalloc( VTX_LIST_SIZE, MEM_ANY );
		VtxLists[1] = MemMalloc( VTX_LIST_SIZE, MEM_ANY );

		GfxLists[0] = MemMalloc( GFX_LIST_SIZE, MEM_ANY );
		GfxLists[1] = MemMalloc( GFX_LIST_SIZE, MEM_ANY );


		Floor_Init( dfb );


		DumpGfxLoaded = TRUE;
	}
}


void FreeDumpGfx()
{
	s32
		i;

	if (DumpGfxLoaded)
	{
		Floor_Deinit();


		MemFree( GfxLists[1] );
		MemFree( GfxLists[0] );

		MemFree( VtxLists[1] );
		MemFree( VtxLists[0] );


		for (i=0; i<10; i++)
			DeinitPlayer( i );


		DumpGfxLoaded = FALSE;
	}
}


void MinOpen(void *arg)
{
	SPlayerInfo
		*pi;
	Gfx
		*gp;
	FH
		file;
	s32
		i,
		j,
		off,
		size;
	u8
		*ptr,
		active;

	ShowWaitScreen();


	AttractMode = FALSE;

#ifndef DEBUG	// attract mode doesn't work in debug 9/10/99 - Hey, i want to see this too, - mjk
	if (fl___gDemoFlag)
	{
		if (DemoMatchCounter & 1)
			AttractMode = TRUE;
	}
#endif

	if (AttractMode)
	{
RunAttract = TRUE;
		ArcadeMode = TRUE;
		AttractPtr = 0;
		AttractFrame = -1;
		SetupAttractText();
	}


	GetPlayoffGame( pcl__gOptions );


	if (!Font)
		Font = AllocAndLoadFile( "PHIL.FNT", MEM_ANY );


	GameBank = (u8 *)Audio_LoadBank( "GameFX.BNK" );

	Speech_Init();


	SetupBoneOffs();


	PlayGfxOpenAnims();
	CamInitGame();
	FreeThrowOpen( NULL );
	reset_commentary();

	if(C3PT)
	{
		Start3Pt();
		camMode = CAM_MODE_3PT_CONTEST;
		time_rate = (1<<8);

		if (Num3PtPlayers == 1)
			SplitScreen = FALSE;
		else
			SplitScreen = TRUE;
	}
	else
	{
		StartGame();
		camMode = CAM_MODE_PRESS;
		SubInitTeams();

		SplitScreen = FALSE;
	}

	if (AttractMode)
		time_rate = (1<<8);

	ShakeScreen = 0;


	i = random( 100 );
	if (i < 29 && !C3PT)
		UseOtherUniform = TRUE;
	else
		UseOtherUniform = FALSE;


	LowModel			= AllocAndLoadFile( "LOW.MDL",			MEM_ANY );
	MediumModel		= AllocAndLoadFile( "MEDIUM.MDL",		MEM_ANY );
	HighModel			= AllocAndLoadFile( "HIGH.MDL",			MEM_ANY );
	ExtremeModel	= AllocAndLoadFile( "extreme.mdl",	MEM_ANY );

ExtremeMtls = AllocAndLoadFile( "bighead.mtl", MEM_ANY );
FixUpMatList( ExtremeMtls, 4 );

BigHeadModel = AllocAndLoadFile( "bighead.mdl", MEM_ANY );
	FaceAnims = AllocAndLoadFile( "Face.ANM", MEM_ANY );

	TestLamps = AllocAndLoadFile( "lamps.obj", MEM_ANY );
	LampMats = AllocAndLoadFile( "lamps.mtl", MEM_ANY );
	FixUpMatList( LampMats, 1 );


	CheckPreNMI();

	Net_Init();
	HotSpot_Init();
	Track_Init();
	Crowd_Init();
	Post_Init( dfb );
	BallGfx_Init( dfb );
	Arrows_Init( dfb );
	Banners_Init( dfb );
	Caption_Init( dfb );

	CheckPreNMI();
	Stadium_Init( dfb );
	CheckPreNMI();

	OverGfx_Init( dfb );
	OverGfx_QuarterInit(0);

	Racks_Init( dfb );


	guScale( &StadiumMat, 2, 2, 2 );

	guTranslate( &LampsMtx, 0, -3000, 0 );


	if (ArcadeMode)
	{
		BallTrail = FALSE;
		ShowTrails = FALSE;

		CloudTex = AllocAndLoadFile( "Cloud.TEX", MEM_ANY );
		NumClouds = 0;
		for (i=0; i<MAX_CLOUDS; i++)
			CloudList[i].w = 0;
	}


	off = 0;
	for (j=0; j<MAX_BONES; j++)
	{
		VertStarts[DETAIL_LOW][j] = off;

		i = *(s32 *)&LowModel[5*4];
		ptr = &LowModel[i + (j * 16)];

		off += ptr[1];
	}

	off = 0;
	for (j=0; j<MAX_BONES; j++)
	{
		VertStarts[DETAIL_MEDIUM][j] = off;

		i = *(s32 *)&MediumModel[5*4];
		ptr = &MediumModel[i + (j * 16)];

		off += ptr[1];
	}

	off = 0;
	for (j=0; j<MAX_BONES; j++)
	{
		VertStarts[DETAIL_HIGH][j] = off;

		i = *(s32 *)&HighModel[5*4];
		ptr = &HighModel[i + (j * 16)];

		off += ptr[1];
	}

	off = 0;
	for (j=0; j<MAX_BONES; j++)
	{
		VertStarts[DETAIL_EXTREME][j] = off;

		i = *(s32 *)&ExtremeModel[5*4];
		ptr = &ExtremeModel[i + (j * 16)];

		off += ptr[1];
	}


	off = 0;
	for (j=0; j<HEAD_NODES; j++)
	{
		HeadStarts[j] = off;

		i = *(s32 *)&BigHeadModel[5*4];
		ptr = &BigHeadModel[i + (j * 16)];

		off += ptr[1];
	}


	for (i=0; i<10; i++)
	{
		active = FALSE;

		if (PracticeMode)
		{
			if (i < 5)
				active = TRUE;
		}
		else
		{
			if (player[i].plyrRAM)
				active = TRUE;
		}

		PlayerInfos[i].Active = active;
	}


	AdjustExtremeModel();


	DumpGfxLoaded = FALSE;

	LoadDumpGfx();


	if (C3PT)
		Speech_PlayByPlay( SFX_54321 );
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

void MinClose(void)
{
	s32
		i;

	stopAllMotors = TRUE;

	if (AttractMode)
		ArcadeMode = TRUE;

	Audio_StopAll();
	Audio_Process();
	Audio_Process();

	FreeDumpGfx();

	if (ArcadeMode)
		MemFree( CloudTex );

	Racks_Deinit();

	OverGfx_Deinit();
	Stadium_Deinit();
	Caption_Deinit();
	Banners_Deinit();
	Arrows_Deinit();
	BallGfx_Deinit();
	Post_Deinit();
	Crowd_Deinit();
	Track_Deinit();
	HotSpot_Deinit();
	Net_Deinit();

	MemFree( LampMats );
	MemFree( TestLamps );

	MemFree( FaceAnims );
	MemFree( BigHeadModel );

	MemFree( ExtremeMtls );

	MemFree( ExtremeModel );
	MemFree( HighModel );
	MemFree( MediumModel );
	MemFree( LowModel );

	if (C3PT)
		End3Pt();

	free_replay_buf();
	FreeThrowClose();
	PlayGfxCloseAnims();

	Speech_Deinit();
	Audio_FreeBank( GameBank );

//	MemFree( Font );

	stopAllMotors = TRUE;
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

int VertexProject2(int x,int y,int z,int *xx,int *yy,int *zz,dyn *d)
{
	float projMatF[4][4];
	float vpScaleXF,vpScaleYF;
	float vpTransXF,vpTransYF;
	float hx,hy,hz,hw;
	int sx,sy,sz;

	vpScaleXF = vpFull.vp.vscale[0] / 4.0;
	vpScaleYF = vpFull.vp.vscale[1] / 4.0;
	vpTransXF = vpFull.vp.vtrans[0] / 4.0;
	vpTransYF = vpFull.vp.vtrans[1] / 4.0;

	guMtxL2F(projMatF,(Mtx *)&d->projection);

	hx = projMatF[0][0] * x +
	     projMatF[1][0] * y +
	     projMatF[2][0] * z +
	     projMatF[3][0];
	hy = projMatF[0][1] * x +
	     projMatF[1][1] * y +
	     projMatF[2][1] * z +
	     projMatF[3][1];
	hz = projMatF[0][2] * x +
	     projMatF[1][2] * y +
	     projMatF[2][2] * z +
	     projMatF[3][2];
	hw = projMatF[0][3] * x +
	     projMatF[1][3] * y +
	     projMatF[2][3] * z +
	     projMatF[3][3];

if (hw == 0)
{
	*zz = 0;
	return( -1 );
}

	hx/=hw;
	hy/=hw;
	hz/=hw;

	if(hz <= 0.0) return(1);
	if(hz >= 1.0) return(1);

	sx=(int)(hx * vpScaleXF + vpTransXF);
	sy=(int)(hy * vpScaleYF + vpTransYF);

	sz=(int)(hz*G_MAXZ/2.0 + G_MAXZ/2.0);
	sz*=32;

//	sz=(hz * 0x7fff);

	*xx=sx;
	*yy=currentProcess->ScreenY-sy;
	*zz=sz;

	return(0);
}


static s32
	DetailAdjust = 0;

static void AdjustDetail()
{
	static s32
		under = 0;

	if (C3PT || Paused || inFreeThrow)
	{
		DetailAdjust = 0;
		return;
	}


	if (FrameTicks > 2)
	{
		// We're running slow...

		under = 0;

		DetailAdjust += 1000;
		if (DetailAdjust > 8000)
			DetailAdjust = 8000;
	}
	else
	{
		// We're running OK...

		if (FrameTime < 128-32)
		{
			under++;

			if (DetailAdjust > 0 && under > (30/2))
				DetailAdjust -= 500;
		}
	}
}


static s32 CalcPlayerDetail( s32 pnum, s32 depth )
{
	playert
		*plyr;
	s32
		adjust;

	if (replay_flag || AttractMode || tip_off_flag)
		adjust = 0;
	else
		adjust = DetailAdjust;


	plyr = &player[pnum];


	if (plyr == bhp)
	{
		if (depth >= 20000-adjust)	return( DETAIL_LOW );
		if (depth >= 12000-adjust)	return( DETAIL_MEDIUM );

		if (replay_flag || AttractMode)
		{
			if (depth >= 6000-adjust)	return( DETAIL_HIGH );

			return( DETAIL_EXTREME );
		}
	}
	else
	{
		if (depth >= 15000-adjust)	return( DETAIL_LOW );
		if (depth >= 8000-adjust)	return( DETAIL_MEDIUM );
	}

	if (depth >= 3500-adjust)
		return( DETAIL_HIGH );

	return( DETAIL_EXTREME );
}


static s32 PlayerInClipVolume( playert *plyr )
{
	vector16
		vec;

	vec.x = plyr->flip_xco>>10;
	vec.y = plyr->flip_zco>>10;
	vec.z = plyr->flip_yco>>10;
	vec.y += 1000;

	View->Front.d -= 2048;
	if (View_Inside( &vec, 1000 ))
	{
		View->Front.d += 2048;
		return( TRUE );
	}

	View->Front.d += 2048;
	return( FALSE );
}


typedef struct SPlayerSort
{
	s32
		Index,
		Depth;
} SPlayerSort;


static SPlayerSort
	SortList[10];

static void CalcPlayerDetails()
{
	SPlayerSort
		temp,
		copy[10];
	SPlayerInfo
		*pi;
	playert
		*plyr;
	vector
		vec;
	s32
		i,
		j,
		depth,
		depths[10];

	pi = PlayerInfos;
	plyr = player;


	if (C3PT)
	{
		for (i=0; i<10; i++)
		{
			SortList[i].Index = i;
			SortList[i].Depth = 4096;

			if (pi[i].Active)
			{
				pi[i].Display = TRUE;

				if (SplitScreen)
					pi[i].DetailLevel = 2;
				else
					pi[i].DetailLevel = 3;
			}
			else
			{
				pi[i].Display = FALSE;
				pi[i].DetailLevel = 0;
			}
		}

		return;
	}


	for (i=0; i<10; i++)
	{
		if (pi->Active && PlayerInClipVolume( plyr ))
		{
			vec.x = (plyr->flip_xco>>10) - View->Camera.x;
			vec.y = (plyr->flip_zco>>10) - View->Camera.y + 1000;
			vec.z = (plyr->flip_yco>>10) - View->Camera.z;
			VecMulMat( &vec, &View->World2Cam, &vec );

			depth = vec.z;
			if (depth < 0)
				depth = 0xffff;
		}
		else
			depth = 0xffff;

		SortList[i].Index = i;
		SortList[i].Depth = depth;

		depths[i] = depth;

		pi++;
		plyr++;
	}


	for (i=0; i<9; i++)
	{
		for (j=(i+1); j<10; j++)
		{
			if (SortList[j].Depth < SortList[i].Depth)
			{
				temp = SortList[i];
				SortList[i] = SortList[j];
				SortList[j] = temp;
			}
		}
	}


	for (i=0; i<10; i++)
	{
		pi = &PlayerInfos[SortList[i].Index];

		if (SortList[i].Depth == 0xffff)
			pi->Display = FALSE;
		else
			pi->Display = TRUE;
	}


	for (i=0; i<10; i++)
	{
		pi = &PlayerInfos[i];

		pi->DetailLevel = CalcPlayerDetail( i, depths[i] );
	}


	for (j=DETAIL_EXTREME; j>DETAIL_HIGH; j--)
	{
		depth = 0;

		for (i=0; i<10; i++)
		{
			pi = &PlayerInfos[SortList[i].Index];

			if (pi->DetailLevel == j)
			{
				depth++;

				if (depth > 3)
					pi->DetailLevel--;
			}
		}
	}


	// Reverse order of extreme models...

	pi = PlayerInfos;
	depth = 0;

	for (i=0; i<10; i++)
	{
		if (pi->DetailLevel == DETAIL_EXTREME)
			depth++;

		pi++;
	}

	if (depth)
	{
		for (i=0; i<10; i++)
			copy[i] = SortList[i];

		for (i=0; i<depth; i++)
			SortList[10-depth+i] = copy[depth-i-1];

		for (i=depth; i<10; i++)
			SortList[i-depth] = copy[i];
	}
}


static EHeadNodeTypes
	HeadNodeTypes[] =
	{
		HEADNODETYPE_NONE,				// Head
		HEADNODETYPE_ROTATE,			// Jaw
		HEADNODETYPE_SCALE_MOVE,	// Mouth
		HEADNODETYPE_SCALE_MOVE,	// Lower Lip
		HEADNODETYPE_SCALE_MOVE,	// Upper Lip
		HEADNODETYPE_MOVE,				// Eyes
		HEADNODETYPE_MOVE					// Brow
	};


static void AnimateHead( const s32 pnum, const u8 *model )
{
	SPlayerInfo
		*pi;
	playert
		*plyr;
	matrix
		mtx;
	vector16
		*v,
		*points;
	s32
		i,
		count;
	u8
		*ptr;
	UFrame
		*anim;

	pi = &PlayerInfos[pnum];

	ptr = &FaceAnims[5*4];
	ptr += PlayerInfos[pnum].FaceAnim * 2*4;

	ptr = &FaceAnims[*(s32 *)&ptr[4]];
	anim = (UFrame *)ptr;
	anim += (PlayerInfos[pnum].FaceFrame >> 8) * (HEAD_NODES-1);


	plyr = &player[pnum];

	for (i=0; i<HEAD_NODES; i++)
	{
		ptr = pi->HeadNodes[i];
if (ptr)
{
		count = *(s32 *)ptr;
		points = (vector16 *)&ptr[4];
		v = &WorkVerts[VertStarts[DETAIL_EXTREME][NODE_HEAD] + HeadStarts[i]];
//		v = &WorkVerts[HeadStarts[i]];

		switch (HeadNodeTypes[i])
		{
			case HEADNODETYPE_NONE:
				while (count)
				{
					v->x = points->x + pi->HeadBases[i].x;
					v->y = points->y + pi->HeadBases[i].y;
					v->z = points->z + pi->HeadBases[i].z;

					v++;
					points++;
					count--;
				}
				break;

			case HEADNODETYPE_ROTATE:
				MakeMatPRH( &mtx, anim->Rotate, 0, 0 );

				while (count)
				{
					Vec16MulMat( points, &mtx, v );
					v->x += pi->HeadBases[i].x;
					v->y += pi->HeadBases[i].y;
					v->z += pi->HeadBases[i].z;

					v++;
					points++;
					count--;
				}
				break;

			case HEADNODETYPE_SCALE_MOVE:
				while (count)
				{
					v->x = (points->x * (u16)anim->Move.Scale) >> 6;

					v->y = points->y;
					v->z = points->z;

					v->x += anim->Move.x;
					v->y += anim->Move.y;
					v->z += anim->Move.z;

					v->x += pi->HeadBases[i].x;
					v->y += pi->HeadBases[i].y;
					v->z += pi->HeadBases[i].z;

					v++;
					points++;
					count--;
				}
				break;

			case HEADNODETYPE_MOVE:
				while (count)
				{
					v->x = points->x;
					v->y = points->y;
					v->z = points->z;

					v->x += anim->Move.x;
					v->y += anim->Move.y;
					v->z += anim->Move.z;

					v->x += pi->HeadBases[i].x;
					v->y += pi->HeadBases[i].y;
					v->z += pi->HeadBases[i].z;

					v++;
					points++;
					count--;
				}
				break;
		}
}

		if (i)
			anim++;
	}
}


static void AnimatePlayer( const s32 pnum, const u8 *model, const s32 detail )
{
	SPlayerInfo
		*pi;
	playert
		*plyr;
	s16
		x,
		y,
		z,
		angles[3];
	Quat
		*qBone,
		*qParent;
	vector16
		*v,
		*points,
		vector;
	s32
		i,
		p,
		body,
		count;
	int
		lf,
		rf;
	u8
		*ptr;

	pi = &PlayerInfos[pnum];
	plyr = &player[pnum];

	EvaluateAngles(plyr, &lf, &rf, plyr->ikBlend);


	body = (plyr->plyrROM->ub___pAcc1 >> 6) & 1;

	pi->NodeLists[detail][NODE_LHAND] = LHandNodes[detail][body][lf];
	pi->MeshLists[detail][MESH_LHAND] = LHandMeshes[detail][body][lf];

	pi->NodeLists[detail][NODE_RHAND] = RHandNodes[detail][body][rf];
	pi->MeshLists[detail][MESH_RHAND] = RHandMeshes[detail][body][rf];


	angles[0] = 0;
	angles[1] = plyr->pitch<<8;
	angles[2] = plyr->flip_angle+0x4000;

	AnglesToQuatPRH(&plyr->rootQuat, angles);
	QuatToMatrix(&plyr->rootMatrix, &plyr->rootQuat);

	for (i=0; i<MAX_BONES; i++)
	{
		p = ParentList[i];

		if (p < 0)			// Hips/Pelvis
		{
			qParent = &plyr->rootQuat;

			if (plyr->flags&MOVE_ROOT)
			{
				plyr->nodeVector[i].x=0;
				plyr->nodeVector[i].z=0;
			}
			else
			{
				plyr->nodeVector[i].x = (plyr->aFrame.x * plyr->scale) >> 8;
				plyr->nodeVector[i].z = (plyr->aFrame.z * plyr->scale) >> 8;
			}

			plyr->nodeVector[i].y = (plyr->aFrame.y * plyr->scale) >> 8;
			Vec16MulMat(plyr->nodeVector+i, &plyr->rootMatrix, plyr->nodeVector+i);

			plyr->nodeVector[i].x += plyr->flip_xco>>10;
			plyr->nodeVector[i].y += plyr->flip_zco>>10;
			plyr->nodeVector[i].z += plyr->flip_yco>>10;
		}
		else
		{
			qParent = plyr->skelQuat+p;

			Vec16MulMat(&BoneOffs[i], plyr->skelMatrix+p, plyr->nodeVector+i);

			plyr->nodeVector[i].x += plyr->nodeVector[p].x;
			plyr->nodeVector[i].y += plyr->nodeVector[p].y;
			plyr->nodeVector[i].z += plyr->nodeVector[p].z;
		}

		if (i == NODE_CHEST)	qParent = &plyr->rootQuat;


		qBone = &plyr->aFrame.q[i];

		QuatMul(plyr->skelQuat+i, qParent, qBone);
		QuatToMatrix( plyr->skelMatrix+i, plyr->skelQuat+i);

		plyr->skelMatrix[i].xX = (plyr->skelMatrix[i].xX * plyr->scale) >> 8;
		plyr->skelMatrix[i].xY = (plyr->skelMatrix[i].xY * plyr->scale) >> 8;
		plyr->skelMatrix[i].xZ = (plyr->skelMatrix[i].xZ * plyr->scale) >> 8;
		plyr->skelMatrix[i].yX = (plyr->skelMatrix[i].yX * plyr->scale) >> 8;
		plyr->skelMatrix[i].yY = (plyr->skelMatrix[i].yY * plyr->scale) >> 8;
		plyr->skelMatrix[i].yZ = (plyr->skelMatrix[i].yZ * plyr->scale) >> 8;
		plyr->skelMatrix[i].zX = (plyr->skelMatrix[i].zX * plyr->scale) >> 8;
		plyr->skelMatrix[i].zY = (plyr->skelMatrix[i].zY * plyr->scale) >> 8;
		plyr->skelMatrix[i].zZ = (plyr->skelMatrix[i].zZ * plyr->scale) >> 8;


		if (i == NODE_HEAD && (bigHeadMode & (1 << plyr->team)))
		{
			plyr->skelMatrix[i].xX <<= 1;
			plyr->skelMatrix[i].xY <<= 1;
			plyr->skelMatrix[i].xZ <<= 1;
			plyr->skelMatrix[i].yX <<= 1;
			plyr->skelMatrix[i].yY <<= 1;
			plyr->skelMatrix[i].yZ <<= 1;
			plyr->skelMatrix[i].zX <<= 1;
			plyr->skelMatrix[i].zY <<= 1;
			plyr->skelMatrix[i].zZ <<= 1;
		}


		if (pi->Display)
		{
			if (detail == DETAIL_EXTREME && i == NODE_HEAD)
			{
				AnimateHead( pnum, pi->HeadModel );

				count = 100;
				v = &WorkVerts[VertStarts[DETAIL_EXTREME][NODE_HEAD]];
				points = v;
			}
			else
			{
				ptr = pi->NodeLists[detail][i];
				if (ptr)
				{
					count = *(s32 *)ptr;
					points = (vector16 *)&ptr[4];
					v = &WorkVerts[VertStarts[detail][i]];
				}
				else
					count = 0;
			}

#if 0
			while (count)
			{
				x = points->x;
				y = points->y;
				z = points->z;

				v->x = (((x * plyr->skelMatrix[i].xX) +
								 (y * plyr->skelMatrix[i].yX) +
								 (z * plyr->skelMatrix[i].zX)) >> 14)
										+ plyr->nodeVector[i].x;

				v->y = (((x * plyr->skelMatrix[i].xY) +
								 (y * plyr->skelMatrix[i].yY) +
								 (z * plyr->skelMatrix[i].zY)) >> 14)
										+ plyr->nodeVector[i].y;

				v->z = (((x * plyr->skelMatrix[i].xZ) +
								 (y * plyr->skelMatrix[i].yZ) +
								 (z * plyr->skelMatrix[i].zZ)) >> 14)
										+ plyr->nodeVector[i].z;

				v++;
				points++;
				count--;
			}
#else
			if (count)
				TransformVectors( points, count, &plyr->skelMatrix[i], &plyr->nodeVector[i], v );
#endif
		}
	}
}


void Net_Init()
{
	s32
		i;

	NetMtls = AllocAndLoadFile( "net.tex", MEM_ANY );
	NetData = AllocAndLoadFile( "net.dat", MEM_ANY );
	NetAnim = AllocAndLoadFile( "NET.ANM", MEM_ANY );

	for (i=0; i<2; i++)
	{
		netanimno[i] = 0;
		netframe[i] = 0;
		netanimrate[i] = 0;
		nethdg[i] = 0;
	}
}


void Net_Deinit()
{
	MemFree( NetAnim );
	MemFree( NetData );
	MemFree( NetMtls );
}


static void Net_Animate()
{
	s32
		net,
		animno,
		frames;

	if (Paused)
		return;


	for (net=0; net<2; net++)
	{
		animno = netanimno[net];

// hack fix for crash bug...
if (animno < 0)
{
	animno = 0;
	netanimno[net] = 0;
}

		if (animno > 0)
			animno--;

		frames = *(s16 *)&NetAnim[(5*4)+(animno*2*2)];

		if (netanimno[net] > 0)
		{
			netframe[net] += netanimrate[net];

			if ((netframe[net] >> 8) >= frames)
			{
				netframe[net] = 0;
				netanimno[net] = 0;
				netanimrate[net] = 0;
				nethdg[net] = 0;
			}
		}
	}
}


static void ScreenGrab()
{
#if 0

	static s32
		held = 0;
	int
		fd;
	s32
		x,
		y,
		zero = 0;
	u16
		rgb;
	u8
		buffer[SCR_X_RES][2];
	s8
		bnum;

	if (ctrlrs[4].ctrlr & CTRL_FIRE_B)
	{
		if (!held)
		{
			held = -1;


			fd = PCcreat( "C:\\N64.TGA", 0 );

			PCwrite( fd, &zero, 2 );

			bnum = 2;
			PCwrite( fd, &bnum, 1 );

			PCwrite( fd, &zero, 4 );
			PCwrite( fd, &zero, 4 );
			PCwrite( fd, &zero, 1 );

			bnum = SCR_X_RES & 255;
			PCwrite( fd, &bnum, 1 );
			bnum = SCR_X_RES >> 8;
			PCwrite( fd, &bnum, 1 );

			bnum = SCR_Y_RES & 255;
			PCwrite( fd, &bnum, 1 );
			bnum = SCR_Y_RES >> 8;
			PCwrite( fd, &bnum, 1 );

			bnum = 16;
			PCwrite( fd, &bnum, 1 );

			PCwrite( fd, &zero, 1 );

			for (y=(SCR_Y_RES-1); y>=0; y--)
			{
				for (x=0; x<SCR_X_RES; x++)
				{
					rgb = cfb[0][(y*SCR_X_RES)+x];
					rgb >>= 1;
					buffer[x][0] = rgb & 255;
					buffer[x][1] = rgb >> 8;
				}

				PCwrite( fd, buffer, SCR_X_RES*2 );
			}

			PCclose( fd );
		}
	}
	else
		held = 0;

#endif
}


extern int camFirstFrame;

static void hack_cam()
{
	static s32
		prev = 0;
	s32
		bits,
		held;

	bits = cont[2];
	held = bits & ~prev;
	prev = bits;

	if (held & CTRL_FIRE_B)
	{
		camTargetNo--;

		if (camSpecialMode != -1)
		{
			if (camTargetNo < -2)
				camTargetNo = 10;
		}
		else
		{
			if (camTargetNo < 0)
				camTargetNo = 10;
		}
	}

	if (held & CTRL_FIRE_A)
	{
		camTargetNo++;

		if (camSpecialMode != -1)
		{
			if (camTargetNo > 10)
				camTargetNo = -2;
		}
		else
		{
			if (camTargetNo > 10)
				camTargetNo = 0;
		}
	}

	if (held & CTRL_C_LEFT)
	{
		camSpecialMode = -1;

		camMode--;

		if (camMode < CAM_MODE_PRESS)
			camMode = CAM_MODE_DUNK_AI;
	}

	if (held & CTRL_C_RIGHT)
	{
		camSpecialMode = -1;

		camMode++;

 		if (camMode > CAM_MODE_DUNK_AI)
			camMode = CAM_MODE_PRESS;
	}

	if (held & CTRL_C_UP)
	{
		camSpecialMode--;
		camFirstFrame = TRUE;
		camSpecialFirstTime = TRUE;
		camSpecialCut = TRUE;

		if (camSpecialMode == CAM_SPECIAL_FREETHROW)
			camSpecialMode--;

		if (camSpecialMode < CAM_SPECIAL_DRIBBLEI)
			camSpecialMode = CAM_SPECIAL_BASTARD;
	}

	if (held & CTRL_C_DOWN)
	{
		camSpecialMode++;
		camFirstFrame = TRUE;
		camSpecialFirstTime = TRUE;
		camSpecialCut = TRUE;

		if (camSpecialMode == CAM_SPECIAL_FREETHROW ||
				camSpecialMode == CAM_SPECIAL_CIRCLE)
			camSpecialMode++;

		if (camSpecialMode > CAM_SPECIAL_NEW1)
			camSpecialMode = CAM_SPECIAL_DRIBBLEI;
	}

	if (bits & CTRL_TRIGGER)
	{
		if (bits & CTRL_SHOULDER_L)
		{
			AttractFrame -= 256;
			if (AttractFrame < 0)
				AttractFrame += (299<<8);
		}

		if (bits & CTRL_SHOULDER_R)
		{
			AttractFrame += 256;
			if (AttractFrame > (299<<8))
				AttractFrame = 0;
		}
	}
	else
	{
		if (held & CTRL_SHOULDER_L)
		{
			AttractFrame -= 256;
			if (AttractFrame < 0)
				AttractFrame = (299<<8);
		}

		if (held & CTRL_SHOULDER_R)
		{
			AttractFrame += 256;
			if (AttractFrame > (299<<8))
				AttractFrame = 0;
		}
	}

	if (bits & CTRL_PAD_UP)
	{
		camZoomUser += 4;
		if (camZoomUser > 200)
			camZoomUser = 200;
	}

	if (bits & CTRL_PAD_DOWN)
	{
		camZoomUser -= 4;
		if (camZoomUser < 0)
			camZoomUser = 0;
	}

	if (held & CTRL_START)
	{
		AttractBasket = AttractBasket ? 0 : -1;
	}
}


void MinProcess(signed int *p)
{
	static s32
		prev = 0;
	s32
		bits,
		held;

	if (AttractMode)
	{
		ProcessAttract();

#if 1
		bits = p[0] | p[1] | p[2] | p[3];
		if (bits)
			currentProcess->status = PROCESS_FREEING;
#endif

//		if (!RunAttract)
//			hack_cam();
	}
	else
	{
//SaveReplay();

		if (C3PT)
			Contest3PtLoop(p);
		else
			main_loop( p );
	}


	Paused = menuFlag | replay_flag;

	Banners_Update();
	Crowd_Animate();
	Stadium_Update();
	Post_Update();
	Net_Animate();
	HotSpot_Update();
	Clouds_Update();


	bits = ctrlrs[2].ctrlr;
	held = bits & ~prev;


#if 0
{
	s32
		i;

	if (held & CTRL_FIRE_A)
	{
		for (i=0; i<5; i++)
			player[i].plyrRAM->sw___pEnergy = 0;
	}

	if (held & CTRL_FIRE_B)
	{
		for (i=0; i<5; i++)
			player[i+5].plyrRAM->sw___pEnergy = 0;
	}

	if (held & CTRL_TRIGGER)
		time_left = ONE_SEC;
}
#endif


	// Animate facial expressions...
	if (!Paused && !AttractMode)
	{
		SPlayerInfo
			*pi;
		u8
			*ptr;
		s32
			i,
			f;

		for (i=0; i<10; i++)
		{
			pi = &PlayerInfos[i];

			if (pi->Active)
			{
#if 0
	if (held & CTRL_TRIGGER)
	{
		pi->FaceAnim = FACEANIM_YELLC+1;
		pi->FaceFrame = 0;
		pi->FaceRate = 1<<8;
#if 0
		switch (i)
		{
			case 9:
				pi->FaceAnim = FACEANIM_YELLC;
				pi->FaceFrame = 0;
				pi->FaceRate = 1<<8;
				break;

			case 6:
				pi->FaceAnim = FACEANIM_GOLDFISH;
				pi->FaceFrame = 0;
				pi->FaceRate = 1<<8;
				break;

			case 4:
				pi->FaceAnim = FACEANIM_SNEER;
				pi->FaceFrame = 0;
				pi->FaceRate = 1<<8;
				break;

			case 2:
				pi->FaceAnim = FACEANIM_SMILEA;
				pi->FaceFrame = 0;
				pi->FaceRate = 1<<8;
				break;
		}
	}

	switch (i)
	{
		case 0:
		case 1:
		case 3:
		case 7:
		case 8:
			if (pi->FaceAnim != FACEANIM_BLINK && !random( 4 ))
			{
				pi->FaceAnim = FACEANIM_BLINK;
				pi->FaceFrame = 0;
				pi->FaceRate = 1<<8;
			}
			break;

		case 5:
			if (pi->FaceAnim != FACEANIM_CHEW)
			{
				pi->FaceAnim = FACEANIM_CHEW;
				pi->FaceFrame = 0;
				pi->FaceRate = 1<<8;
			}
			break;
#endif
	}
#endif
				if (pi->FaceAnim)
				{
					ptr = &FaceAnims[5*4];
					ptr += (pi->FaceAnim * 2*4);
					f = pi->FaceFrame;
					f += pi->FaceRate;
					if ((f>>8) >= *(s32 *)ptr)
					{
						f = 0;
						pi->FaceAnim = 0;
					}
					pi->FaceFrame = f;
				}
			}
		}
	}


	prev = bits;
}



/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

static Gfx *DrawChar( Gfx *gp, s8 c, s32 x, s32 y )
{
	s32
		s,
		t;

	x *= 8;
	y *= 16;

	c -= ' ';
	s = (s32)(c & 15) * 8;
	t = (s32)(c / 16) * 8;

	gSPTextureRectangle( gp++, x<<2, y<<2, (x+8)<<2, (y+12)<<2, 0, s<<5, t<<5, (1<<10), (7<<10)/12 );

	return( gp );
}


static Gfx * DrawString(Gfx *gp, const char *string, s32 x, s32 y)
{
	char
		c;

	while (*string)
	{
		c = *string++;
		if (c > 0x60)		c -= 0x20;

		gp = DrawChar( gp, c, x, y );
		x++;
	}

	return( gp );
}


static Gfx *DrawNumber( Gfx *gp, s32 number, s32 x, s32 y )
{
	s32
		digit,
		zero = FALSE,
		div = 1000000;

	if (number < 0)
	{
		gp = DrawChar( gp, '-', x, y );
		x++;
		number = -number;
	}

	while (div > 0)
	{
		digit = number / div;
		number -= (digit * div);
		div /= 10;

		if (digit == 0)
		{
			if (div == 0)
				zero = TRUE;
		}
		else
			zero = TRUE;

		if (digit || zero)
		{
			gp = DrawChar( gp, digit+'0', x, y );
			x++;
		}
	}

	return( gp );
}


static Gfx *DrawHexNum( Gfx *gp, u32 number, s32 x, s32 y )
{
	s32
		i,
		digit,
		zero = FALSE;

	if (number == 0)
	{
		gp = DrawChar( gp, '0', x, y );
		return( gp );
	}

	for (i=0; i<8; i++)
	{
		digit = (number >> (32-4)) & 15;
		number <<= 4;

		if (digit)
		{
			zero = TRUE;
			if (digit > 9)
				digit += 'A' - '9' - 1;
		}

		if (digit || zero)
		{
			gp = DrawChar( gp, digit+'0', x, y );
			x++;
		}
	}

	return( gp );
}


static Gfx *DrawTextAt3D( Gfx *gp, dyn *d, char *text, int x, int y, int z )
{
	char
		c;
	s32
		s,
		t;

	if (!VertexProject2( x, y, z, &x, &y, &z , d ) && z > 64)
	{
		while (*text)
		{
			c = *text++;
			if (c > 0x60)		c -= 0x20;

			c -= ' ';
			s = (s32)(c & 15) * 8;
			t = (s32)(c / 16) * 8;

			gSPTextureRectangle( gp++, x<<2, y<<2, (x+8)<<2, (y+12)<<2, 0, s<<5, t<<5, (1<<10), (7<<10)/12 );

			x += 8;
		}
	}

	return( gp );
}


static void CalcNet( s32 net, s32 pos )
{
	matrix
		mtx;
	s16
		*ptr,
		*anim,
		count;
	s32
		i,
		s,
		x,
		y,
		z,
		animno;
	vector16
		vec,
		*v,
		*base,
		*points;

	base = Post_GetBase();


	animno = netanimno[net];

// hack fix for crash bug...
if (animno < 0)
	animno = 0;

	if (animno > 0)
		animno--;

	ptr = (s16 *)&NetAnim[(5*4)+(animno*2*2)];


	MakeMatPRH( &mtx, 0, 0, nethdg[net] + (net << 15) );

	anim = (s16 *)&NetAnim[ptr[1]];
	anim += (netframe[net] >> 8) * 3 * 4;

#ifdef DEBUG
	{
		s32
			frames;

		frames = *(s16 *)&NetAnim[(5*4)+(animno*2*2)];

		if (netframe[net] < 0 || netframe[net] >= (frames<<8))
			FatalError( "bad net frame", __FILE__, __LINE__ );
	}
#endif

	ptr = (s16 *)&NetData[*(s32 *)&NetData[6*4]];
	points = (vector16 *)&NetData[*(s32 *)&NetData[5*4]];
	v = WorkVerts;

	for (i=0; i<3; i++)
	{
		vec.x = anim[0];
		vec.y = anim[1];
		vec.z = anim[2];
		Vec16MulMat( &vec, &mtx, &vec );

		x = vec.x - base->x;
		y = vec.y - base->y + (BASKET_Z>>10);
		z = vec.z - base->z - ((GLASS_Y - BASKET_Y) >> 10) - 96;

		s = anim[3];
		anim += 4;

		count = *ptr++;
		while (count)
		{
			v->x = x + (((s32)points->x * s) >> 12);
			v->y = y + points->y;
			v->z = z + (((s32)points->z * s) >> 12);

			v++;
			points++;
			count--;
		}
	}

	while (v != &WorkVerts[48])
	{
		v->x = points->x - base->x;
		v->y = points->y - base->y + (BASKET_Z>>10);
		v->z = points->z - base->z - ((GLASS_Y - BASKET_Y) >> 10) - 96;

		v++;
		points++;
	}
}


static Gfx *DrawNet( Gfx *gp, s32 net, Vtx *verts, dyn *d )
{
	VertexN64
		*v = (VertexN64 *)verts;
	s16
		count,
		command,
		*cmd;
	u8
		*ptr,
		*vtx,
		*tex;
	u32
		*uv;
	s16
		i;

	tex = NetMtls;

	gDPPipeSync( gp++ );
	gDPSetAlphaCompare( gp++, G_AC_NONE );
	gDPSetTextureLUT( gp++, G_TT_NONE );

//	gSPMatrix( gp++, &d->identity, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_PUSH );
	if (net < 2)
	{
		gSPMatrix( gp++, &d->PostMats[net], G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );
	}
	else
	{
		vector16
			*base;

		base = Post_GetBase();

		d->FreeThrowNet = d->PostMats[net & 1];
		if (net == 2)
		{
			SetTransN64( &d->FreeThrowNet,
									 base->x + freeThrowX,
									 base->y,
									 +(base->z + (GLASS_Y>>10) + 96) );
		}
		else
		{
			SetTransN64( &d->FreeThrowNet,
									 base->x + freeThrowX,
									 base->y,
									 -(base->z + (GLASS_Y>>10) + 96) );
		}

		gSPMatrix( gp++, &d->FreeThrowNet, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );
	}

if (1)
{
	gDPSetCycleType( gp++, G_CYC_2CYCLE );
	gDPSetCombineMode( gp++, G_CC_TRILERP, G_CC_PASS2 );
	gDPSetRenderMode( gp++, G_RM_NOOP, G_RM_ZB_XLU_SURF2 );
//gDPSetRenderMode( gp++, G_RM_NOOP, G_RM_ZB_OPA_SURF2 );
	gDPSetTextureLOD( gp++, G_TL_LOD );

	gSPTexture( gp++, 0x8000, 0x8000, 3, G_TX_RENDERTILE, G_ON );

	gDPLoadTextureBlock_4b( gp++,
													tex,
													G_IM_FMT_IA,
													64,
													64,
													0,
													G_TX_WRAP,
													G_TX_WRAP,
													6, 6,
													G_TX_NOLOD, G_TX_NOLOD );

	gDPSetTile( gp++,
							G_IM_FMT_IA,
							G_IM_SIZ_4b,
							64/16,
							0,
							0,		// Tile
							0,
							G_TX_WRAP,
							6,
							G_TX_NOLOD,
							G_TX_WRAP,
							5,
							G_TX_NOLOD );
	gDPSetTileSize( gp++, 0, 0, 0, (32-1)<<2, (64-1)<<2 );

	gDPSetTile( gp++,
							G_IM_FMT_IA,
							G_IM_SIZ_4b,
							64/16,
							(32/16),		// Tmem
							1,		// Tile
							0,
							G_TX_WRAP,
							5,
							1,
							G_TX_WRAP,
							4,
							1 );
	gDPSetTileSize( gp++, 1, 0, 0, (16-1)<<2, (32-1)<<2 );

	gDPSetTile( gp++,
							G_IM_FMT_IA,
							G_IM_SIZ_8b,
							64/16,
							(32+16)/16,		// Tmem
							2,		// Tile
							0,
							G_TX_WRAP,
							4,
							2,
							G_TX_WRAP,
							3,
							2 );
	gDPSetTileSize( gp++, 2, 0, 0, (8-1)<<2, (16-1)<<2 );

	gDPSetTile( gp++,
							G_IM_FMT_IA,
							G_IM_SIZ_8b,
							64/16,
							((16*64)+32+16)/16,		// Tmem
							3,		// Tile
							0,
							G_TX_WRAP,
							3,
							3,
							G_TX_WRAP,
							2,
							3 );
	gDPSetTileSize( gp++, 3, 0, 0, (4-1)<<2, (8-1)<<2 );
}
else
{
	gDPSetCycleType( gp++, G_CYC_1CYCLE );
	gDPSetCombineMode( gp++, G_CC_DECALRGBA, G_CC_DECALRGBA );
	gDPSetRenderMode( gp++, G_RM_ZB_XLU_SURF, G_RM_ZB_XLU_SURF2 );
//gDPSetRenderMode( gp++, G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2 );
	gDPSetTextureLOD( gp++, G_TL_TILE );

	gSPTexture( gp++, 0x8000, 0x8000, 0, G_TX_RENDERTILE, G_ON );

	gDPLoadTextureBlock_4b( gp++,
													tex,
													G_IM_FMT_CI,
													64,
													64,
													0,
													G_TX_WRAP,
													G_TX_WRAP,
													6, 6,
													G_TX_NOLOD, G_TX_NOLOD );

	gDPSetTile( gp++,
							G_IM_FMT_IA,
							G_IM_SIZ_4b,
							64/16,
							((16*64)+32+16)/16,		// Tmem
							0,		// Tile
							0,
							G_TX_WRAP,
							3,
							3,
							G_TX_WRAP,
							2,
							3 );
	gDPSetTileSize( gp++, 3, 0, 0, (4-1)<<2, (8-1)<<2 );
}


	cmd = (s16 *)&NetData[*(s32 *)&NetData[4*4]];
	uv = (u32 *)&NetData[*(s32 *)&NetData[7*4]];

	while (*cmd != 0)
	{
		switch (*cmd++)
		{
			case CMD_VERTICES:
				count = *cmd++;
				for (i=0; i<count; i++)
				{
					*(u64 *)&v[i] = *(u64 *)&WorkVerts[*cmd++];
					*(u32 *)&v[i].u = *uv++;
					*(s32 *)&v[i].r = -1;
				}

				gSPVertex( gp++, v, count, 0 );
				v += count;
				break;

			case CMD_FACES:
				count = *cmd++;

				while (count >= 2)
				{
					ptr = (u8 *)cmd;
					gSP2Triangles( gp++,
												 ptr[0], ptr[1], ptr[2], 0,
												 ptr[4], ptr[5], ptr[6], 0 );
					cmd += (2*4/2);
					count -= 2;
				}
				if (count)
				{
					ptr = (u8 *)cmd;
					gSP1Triangle( gp++, ptr[0], ptr[1], ptr[2], 0 );
					cmd += (1*4/2);
				}
				break;
		}
	}

	gDPPipeSync( gp++ );
	gDPSetCycleType( gp++, G_CYC_1CYCLE );
	gDPSetTextureLOD( gp++, G_TL_TILE );
gDPSetTextureLUT( gp++, G_TT_RGBA16 );

	return( gp );
}


void FlipPlayerCoords(void)
{
	int i;

	if(switch_ends_flag)
	{
		for(i=0;i<11;i++)
		{
			player[i].flip_xco=-player[i].xco;
			player[i].flip_yco=-player[i].yco;
			player[i].flip_zco=player[i].zco;
			player[i].flip_angle=(signed short)(player[i].angle^32768);
		}
	}
	else
	{
		for(i=0;i<11;i++)
		{
			player[i].flip_xco=player[i].xco;
			player[i].flip_yco=player[i].yco;
			player[i].flip_zco=player[i].zco;
			player[i].flip_angle=player[i].angle;
		}
	}
}


//static camPos
//	camCurrent = { 0, 0, 0, 0, 0 },
//	camIdeal = { 0, 0, 0, 0, 0 };


// Hack for Mike's points...

#define MAX_POINTS 32
#define POINT_SIZE 64


static s32
	PlotCol = -1,
	NumPoints = 0,
	PointsCol[MAX_POINTS];

static vector16
	PointsPos[MAX_POINTS];


void PlotPoint( s32 x, s32 y, s32 z )
{
	if (NumPoints < MAX_POINTS)
	{
		PointsPos[NumPoints].x = x;
		PointsPos[NumPoints].y = y;
		PointsPos[NumPoints].z = z;

		PointsCol[NumPoints] = PlotCol;

		NumPoints++;
	}
}


static Gfx *DrawPoints( Gfx *gp, dyn *d )
{
	static Vtx
		verts[8] =
		{
			-POINT_SIZE-8, +POINT_SIZE-8, 0, 0, 0, 0, 255, 255, 255, 255,
			-POINT_SIZE+8, +POINT_SIZE+8, 0, 0, 0, 0, 255, 255, 255, 255,
			+POINT_SIZE+8, -POINT_SIZE+8, 0, 0, 0, 0, 255, 255, 255, 255,
			+POINT_SIZE-8, -POINT_SIZE-8, 0, 0, 0, 0, 255, 255, 255, 255,

			+POINT_SIZE-8, +POINT_SIZE+8, 0, 0, 0, 0, 255, 255, 255, 255,
			+POINT_SIZE+8, +POINT_SIZE-8, 0, 0, 0, 0, 255, 255, 255, 255,
			-POINT_SIZE+8, -POINT_SIZE-8, 0, 0, 0, 0, 255, 255, 255, 255,
			-POINT_SIZE-8, -POINT_SIZE+8, 0, 0, 0, 0, 255, 255, 255, 255,
		};
	s32
		i;

	gDPPipeSync( gp++ );
	gSPTexture( gp++, 0x8000, 0x8000, 0, 0, G_OFF );
//	gDPSetCombineMode( gp++, G_CC_SHADE, G_CC_SHADE );
gDPSetCombineMode( gp++, G_CC_PRIMITIVE, G_CC_PRIMITIVE );
	gDPSetRenderMode( gp++, G_RM_OPA_SURF, G_RM_OPA_SURF2 );
	gDPSetTextureLUT( gp++, G_TT_NONE );

	for (i=0; i<NumPoints; i++)
	{
		MatToN64( &View->Cam2World, &d->PointMats[i] );
		SetTransN64( &d->PointMats[i], PointsPos[i].x, PointsPos[i].y, PointsPos[i].z );
		gSPMatrix( gp++, &d->PointMats[i], G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_PUSH );

		gSPVertex( gp++, verts, 8, 0 );

		gDPPipeSync( gp++ );
		gDPSetPrimColor( gp++, 0, 0,
										 (PointsCol[i] >> 24) & 255,
										 (PointsCol[i] >> 16) & 255,
										 (PointsCol[i] >> 8) & 255,
										 255 );

		gSP2Triangles( gp++, 0, 2, 1, 0, 0, 3, 2, 0 );
		gSP2Triangles( gp++, 4, 6, 5, 0, 4, 7, 6, 0 );

		gSPPopMatrix( gp++, G_MTX_MODELVIEW );
	}

	gDPPipeSync( gp++ );
	gSPTexture( gp++, 0x8000, 0x8000, 0, G_TX_RENDERTILE, G_ON );
	gDPSetTextureLUT( gp++, G_TT_RGBA16 );
gDPSetPrimColor( gp++, 0, 0, 255, 255, 255, 255 );

	NumPoints = 0;

	return( gp );
}


// Hack for Noah's lines...

#define MAX_LINES 64


s32
	NumLines = 0;

Vtx
	LineVerts[MAX_LINES][2];


void PlotLine( s32 x1, s32 y1, s32 z1, s32 x2, s32 y2, s32 z2, u8 r, u8 g, u8 b )
{
	VertexN64
		*v;

#if !defined(DEBUG)
	return;
#endif

	if (NumLines < MAX_LINES)
	{
		v = (VertexN64 *)&LineVerts[NumLines][0];

		v->x = x1>>10;
		v->y = z1>>10;
		v->z = y1>>10;

		v->r = r;
		v->g = g;
		v->b = b;
		v->a = 255;

		v++;
		v->x = x2>>10;
		v->y = z2>>10;
		v->z = y2>>10;

		v->r = r;
		v->g = g;
		v->b = b;
		v->a = 255;

		NumLines++;
	}
}


static Gfx *DrawLines( Gfx *gp )
{
#if	!defined(NO_DEBUG_STUFF) || defined(NOAH)

	s32
		i;

	gSPTexture( gp++, 0x8000, 0x8000, 0, 0, G_OFF );
	gDPSetCombineMode( gp++, G_CC_SHADE, G_CC_SHADE );
	gDPSetRenderMode( gp++, G_RM_AA_XLU_LINE, G_RM_AA_XLU_LINE2 );

	for (i=0; i<NumLines; i++)
	{
		gSPVertex( gp++, &LineVerts[i][0], 2, 0 );
		gSPLine3D( gp++, 0, 1, 0 );
	}

#endif

	NumLines = 0;

	return( gp );
}


#define	RM_ZTEST(clk)					\
	Z_CMP | CVG_DST_FULL | ALPHA_CVG_SEL |		\
	ZMODE_OPA |						\
	GBL_c##clk(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)

#define	G_RM_ZTEST	RM_ZTEST(1)
#define	G_RM_ZTEST2	RM_ZTEST(2)


static Gfx *DrawPlayer( Gfx *gp, const s32 pnum, const s32 db, const s32 detail )
{
	SPlayerInfo
		*pi;
	VertexN64
		*v;
	SMaterial
		*mtl;
	u8
		order[MESH_COUNT];
	u32
		*vptr;
	s32
		a,
		i,
		m;
	u16
		cmd,
		cnt,
		*ptr,
		*cptr;

	pi = &PlayerInfos[pnum];

	v = VtxPtr;
	pi->GfxList = gp;


	if (detail == DETAIL_EXTREME)
	{
		order[0] = MESH_BODY;
		order[1] = MESH_LHAND;
		order[2] = MESH_RHAND;
		order[3] = MESH_HEAD;
	}
	else
	{
		order[0] = MESH_HEAD;
		order[1] = MESH_BODY;
		order[2] = MESH_LHAND;
		order[3] = MESH_RHAND;
	}


	for (m=0; m<MESH_COUNT; m++)
	{
		i = order[m];

		vptr = pi->MeshLists[detail][i].TexCoords;
		cptr = pi->MeshLists[detail][i].Commands;
if (vptr)
{
		cmd = -1;
		while (cmd != CMD_END)
		{
			cmd = cptr[0];
			cnt = cptr[1];
			cptr += 2;

			switch (cmd)
			{
				case CMD_TEXTURE:
					switch (detail)
					{
						case DETAIL_LOW:
							mtl = &pi->MtlLists[TEXSET_LOW][cnt];
							break;

						case DETAIL_MEDIUM:
							mtl = &pi->MtlLists[TEXSET_HIGH][cnt];
							break;

						case DETAIL_HIGH:
							mtl = &pi->MtlLists[TEXSET_HIGH][cnt];
							break;

						case DETAIL_EXTREME:
							mtl = &pi->MtlLists[TEXSET_EXTREME][cnt];
							break;
					}

					if (mtl->Texture)
					{
						gDPLoadTextureBlock( gp++,
																 mtl->Texture,
																 G_IM_FMT_CI,
																 G_IM_SIZ_8b,
																 mtl->Width,
																 mtl->Height,
																 0,
																 mtl->Flags.Xflags,
																 mtl->Flags.Yflags,
																 mtl->Flags.Xmask,
																 mtl->Flags.Ymask,
																 G_TX_NOLOD,
																 G_TX_NOLOD );
					}

					if (mtl->Palette)
					{
						gDPLoadTLUT_pal256( gp++, mtl->Palette );
					}
					break;

				case CMD_VERTICES:
					gSPVertex( gp++, v, cnt, 0 );

					while (cnt > 0)
					{
						*(u64 *)&v->x = *(u64 *)&WorkVerts[*cptr++];
						*(u32 *)&v->u = *vptr++;
						*(s32 *)&v->r = 0xe6e6e6ff;

						v++;
						cnt--;
					}
					break;

				case CMD_FACES:
#if 1
					while (cnt >= 2)
					{
						gSP2Triangles( gp++,
													 cptr[0], cptr[1], cptr[2], 0,
													 cptr[3], cptr[4], cptr[5], 0 );
						cptr += 2*3;
						cnt -= 2;
					}
					if (cnt)
					{
						gSP1Triangle( gp++, cptr[0], cptr[1], cptr[2], 0 );
						cptr += 3;
					}
#else
					while (cnt)
					{
						gSP1Triangle( gp++, cptr[0], cptr[1], cptr[2], 0 );
						cptr += 3;
						cnt--;
					}
#endif
					break;

				case CMD_ALPHA:
					gDPPipeSync( gp++ );
					gSPClearGeometryMode( gp++, G_CULL_BACK );
					gSPSetGeometryMode( gp++, G_CULL_FRONT );
					gDPSetRenderMode( gp++, G_RM_ZB_XLU_SURF, G_RM_ZB_XLU_SURF2 );
					break;

				case CMD_OPAQUE:
					gDPPipeSync( gp++ );
					gSPClearGeometryMode( gp++, G_CULL_FRONT );
					gSPSetGeometryMode( gp++, G_CULL_BACK );
					gDPSetRenderMode( gp++, G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2 );
					break;

				case CMD_NEGVERTS:
					gSPVertex( gp++, v, cnt, 0 );

					while (cnt > 0)
					{
						*(u64 *)&v->x = *(u64 *)&WorkVerts[*cptr++];
						*(u32 *)&v->u = *vptr++;
						*(s32 *)&v->r = 0xe6e6e6ff;

						v->y = -v->y;
						a = 144 + (v->y >> 2);
						if (a < 0)
							a = 0;
						v->a = (u8)a;

						v++;
						cnt--;
					}
					break;

#if 1
				case CMD_ZTEST:
//					gDPPipeSync( gp++ );
					gDPSetRenderMode( gp++, G_RM_ZTEST, G_RM_ZTEST2 );
					break;

				case CMD_ZUPDATE:
					gDPPipeSync( gp++ );
					gDPSetRenderMode( gp++, G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2 );
					break;
#endif
			}
		}
}
	}

	gSPEndDisplayList( gp++ );

	VtxPtr = v;


	return( gp );
}


static Gfx *DrawStadium( Gfx *gp, int db, dyn *d )
{
	s32
		i;

	gSPMatrix( gp++, &StadiumMat, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );

	gp = Stadium_DrawRoof( gp, db );

	gDPPipeSync( gp++ );
	gSPMatrix( gp++, &StadiumMat, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );
	gp = Stadium_DrawEntry( gp );


gDPSetBlendColor( gp++, 0, 0, 0, 255 );
gDPSetPrimColor( gp++, 0, 0, 0, 0, 0, 255 );
	gSPMatrix( gp++, &d->identity, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );
	gp = Floor_DrawWood( gp );


	if (!C3PT)
	{
		gDPPipeSync( gp++ );
		gDPSetAlphaCompare( gp++, G_AC_NONE );
		gSPMatrix( gp++, &LampsMtx, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );
		gDPSetRenderMode( gp++, G_RM_XLU_SURF, G_RM_XLU_SURF2 );
		gDPSetCombineMode( &TexModeCombine, G_CC_DECALRGBA, G_CC_DECALRGBA );
		gp = DrawObject( gp, TestLamps, LampMats );
	}


	gDPPipeSync( gp++ );
	gDPSetAlphaCompare( gp++, G_AC_NONE );
	gDPSetRenderMode( gp++, G_RM_XLU_SURF, G_RM_XLU_SURF2 );
	gp = Post_DrawReflect( gp, 0, db, d );
	gp = Post_DrawReflect( gp, 1, db, d );


	gDPPipeSync( gp++ );
	gSPMatrix( gp++, &StadiumMat, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );
	gp = Stadium_DrawMain( gp );


	gp = Banners_Draw( gp, d );


gDPPipeSync( gp++ );
	gSPMatrix( gp++, &d->identity, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );
	gp = Floor_DrawLines( gp );

	if (!inFreeThrow)
	{
		gp = HotSpot_Draw( gp, db );
		gp = Arrows_Draw( gp, d );
	}


	gDPPipeSync( gp++ );
	gSPSetGeometryMode(gp++,G_ZBUFFER);
	gDPSetDepthSource(gp++,G_ZS_PIXEL);

	gDPSetAlphaCompare( gp++, G_AC_NONE );


	gp = Racks_Draw( gp );


	gDPSetRenderMode( gp++, G_RM_RA_ZB_OPA_SURF, G_RM_RA_ZB_OPA_SURF2 );
	gp = Post_DrawMain( gp, 0, db, d );
	gp = Post_DrawMain( gp, 1, db, d );


	return( gp );
}


static SCamera
	*CamPtr,
	Cameras[2];


static void Update3PointCamera( playert *plyr, SCamera *cam )
{
	matrix
		mat;
	vector16
		vec;
	s32
		x,
		z,
		basket;

	x = (plyr->flip_xco >> 10);
	z = (plyr->flip_yco >> 10);

	if (z > 0)
		basket = +BASKET_Y>>10;
	else
		basket = -BASKET_Y>>10;

	cam->x = x;
	cam->y = 800<<2;//3584>>2;
	cam->z = z;

	cam->Pitch = 1920;
	cam->Yaw = LFPAtn( basket - z, x );

	MakeMatPRH( &mat, 0, 0, cam->Yaw );
	vec.x = 0;
	vec.y = 0;
	vec.z = -1400<<2;
	Vec16MulMat( &vec, &mat, &vec );

	cam->x += vec.x;
	cam->z += vec.z;
}


static Gfx *SetScissor( Gfx *gp )
{
#if 0
	if (camLetterBox)
	{
		gDPSetScissor( gp++,
									 G_SC_NON_INTERLACE,
									 0, 36,
									 currentProcess->ScreenX,
									 currentProcess->ScreenY - 36 );
	}
	else
	{
#endif
		gDPSetScissor( gp++,
									 G_SC_NON_INTERLACE,
									 0, 0,
									 currentProcess->ScreenX,
									 currentProcess->ScreenY );
//	}

	return( gp );
}



void MinRender(dyn *d,int draw_buffer,Gfx **gl)
{
	SPlayerInfo
		*pi;
	Gfx
		*gp;
	Mtx
		matp,
		matt,
		matl,
		cmat;
	matrix
		matz;
	vector16
		trans;
	s32
		i,
		which,
		detail;
	u16
		perspnorm,
		perspnorm2;

	AdjustDetail();
	Speech_Process();
	Crowd_Update();


	which = draw_buffer;

	// copy ptr
	gp=(*gl);


	// create identity matrix
	guMtxIdent( &d->identity );


	if (menuFlag && (si___gCurState || si___gNxtState))
	{
		SetVidMode( VIDMODE_SHELL );

		// initialise RCP
		gSPSegment(gp++, 0, 0x0);
		gSPDisplayList(gp++, DLInitRDP);
		gSPDisplayList(gp++, DLInitRSP);

		// set scissor rect
		gDPSetScissor(gp++,G_SC_NON_INTERLACE,0,0,512,240);

		// clear framebuffer
		gDPPipeSync(gp++);
		gDPSetCycleType(gp++,G_CYC_FILL);
		gDPSetColorImage(gp++,G_IM_FMT_RGBA, G_IM_SIZ_16b,512,cfb[draw_buffer]);
		gDPSetFillColor(gp++,GPACK_RGBA5551(0,0,0,255) << 16 | GPACK_RGBA5551(0,0,0,255));
		gDPFillRectangle(gp++,0, 0, 512-1, 240-1);

		*gl = gp;

		ShellRender( d, draw_buffer, gl );

		return;
	}
	else
		SetVidMode( VIDMODE_GAME );


	Post_Setup( d );


//	ScreenGrab();


	if (C3PT)
	{
		s32
			y;

		Update3PointCamera( &player[Human3PtMatchPlayers[0]], &Cameras[0] );
		si___gCamThi = Cameras[0].Pitch;
		si___gCamTheta = Cameras[0].Yaw;

//		if (Num3PtPlayers > 1)
		{
			playert
				*plyr;

if (Num3PtPlayers > 1)
{
			plyr = &player[Human3PtMatchPlayers[1]];
			Update3PointCamera( plyr, &Cameras[1] );
}
else
{
			plyr = &player[Human3PtMatchPlayers[0]];
			y = plyr->flip_yco;
			plyr->flip_yco = -y;
			Update3PointCamera( plyr, &Cameras[1] );
			plyr->flip_yco = y;
		}
}

		if (SplitScreen)
		{
			View_Calc( &Cameras[0], 44/2, 32, &Views[0] );
			View_Calc( &Cameras[1], 44/2, 32, &Views[1] );
		}
		else
			View_Calc( &Cameras[0], 44, 32, &Views[0] );

		View_Set( &Views[0] );
	}
	else
	{
		Cameras[0].Yaw = si___gCamTheta;
		Cameras[0].Pitch = si___gCamThi;
		Cameras[0].x = si___gCamX >> 14;
		Cameras[0].y = si___gCamZ >> 14;
		Cameras[0].z = si___gCamY >> 14;

		View_Calc( &Cameras[0], 44, 32, &Views[0] );
		View_Set( &Views[0] );
	}


	MakeMatPRH( &matz, -Cameras[0].Pitch, 0, -Cameras[0].Yaw );
	MatInvert( &matz );

	trans.x = (s16)-Cameras[0].x >> 2;
	trans.y = (s16)-Cameras[0].y >> 2;
	trans.z = (s16)Cameras[0].z >> 2;
	Vec16MulMat( &trans, &matz, &trans );

	MatToN64( &matz, &cmat );
	SetTransN64( &cmat, trans.x, trans.y, trans.z );


	// initialise RCP
	gSPSegment(gp++, 0, 0x0);
	gSPDisplayList(gp++, DLInitRDP);
	gSPDisplayList(gp++, DLInitRSP);


#if 0
	if (camLetterBox)
	{
		gDPSetScissor( gp++, G_SC_NON_INTERLACE, 0, 0, currentProcess->ScreenX, currentProcess->ScreenY );
		gDPSetCycleType( gp++, G_CYC_FILL );
		gDPSetColorImage( gp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, currentProcess->ScreenX, cfb[draw_buffer] );
		gDPSetFillColor( gp++, GPACK_RGBA5551(0,0,0,255) << 16 | GPACK_RGBA5551(0,0,0,255) );
		gDPFillRectangle( gp++, 0, 0, currentProcess->ScreenX-1, 36-1 );
		gDPFillRectangle( gp++, 0, currentProcess->ScreenY-36, currentProcess->ScreenX-1, currentProcess->ScreenY-1 );
	}
#endif

	// set scissor rect
	gp = SetScissor( gp );

	// define viewport
	if (SplitScreen)
	{
		vpLeft.vp.vscale[0] = currentProcess->ScreenX/2*4;
		vpLeft.vp.vscale[1] = currentProcess->ScreenY/2*4;
		vpLeft.vp.vtrans[0] = currentProcess->ScreenX/4*4;
		vpLeft.vp.vtrans[1] = currentProcess->ScreenY/2*4;
		gSPViewport( gp++, &vpLeft );

		vpRight.vp.vscale[0] = currentProcess->ScreenX/2*4;
		vpRight.vp.vscale[1] = currentProcess->ScreenY/2*4;
		vpRight.vp.vtrans[0] = currentProcess->ScreenX*3/4*4;
		vpRight.vp.vtrans[1] = currentProcess->ScreenY/2*4;
	}
	else
	{
		vpFull.vp.vscale[0]=currentProcess->ScreenX/2*4;
		vpFull.vp.vscale[1]=currentProcess->ScreenY/2*4;
		vpFull.vp.vtrans[0]=currentProcess->ScreenX/2*4;
		vpFull.vp.vtrans[1]=currentProcess->ScreenY/2*4;

		if (ArcadeMode && ShakeScreen)
		{
			vpFull.vp.vtrans[0] += ShakeScreen * (random( 2 ) ? +1 : -1);
			vpFull.vp.vtrans[1] += ShakeScreen * (random( 2 ) ? +1 : -1);

			ShakeScreen--;
		}

#if 0
		if (camLetterBox)
			vpFull.vp.vscale[1] = vpFull.vp.vscale[1] * 3 / 4;
#endif

		gSPViewport(gp++,&vpFull);
	}

	guPerspective( &matp, &perspnorm, FOV, (float)VID_X_RES / (float)VID_Y_RES, NEAR_Z+2, FAR_Z+2, 1 );
	guScale( &matl, 0.25F, 0.25F, -0.25F );
	guMtxCatL( &matl, &cmat, &matt );
	guMtxCatL( &matt, &matp, &d->BallProject );

	guPerspective( &matp, &perspnorm, FOV, (float)VID_X_RES / (float)VID_Y_RES, NEAR_Z, FAR_Z, 1 );
	guScale( &matl, 0.25F, 0.25F, -0.25F );
	guMtxCatL( &matl, &cmat, &matt );
	guMtxCatL( &matt, &matp, &d->projection );

	gSPPerspNormalize( gp++, perspnorm );


	if (SplitScreen)
	{
		MakeMatPRH( &matz, -Cameras[1].Pitch, 0, -Cameras[1].Yaw );
		MatInvert( &matz );

		trans.x = (s16)-Cameras[1].x >> 2;
		trans.y = (s16)-Cameras[1].y >> 2;
		trans.z = (s16)Cameras[1].z >> 2;
		Vec16MulMat( &trans, &matz, &trans );

		MatToN64( &matz, &cmat );
		SetTransN64( &cmat, trans.x, trans.y, trans.z );

		guPerspective( &matp, &perspnorm, FOV, (float)VID_X_RES / (float)VID_Y_RES, NEAR_Z, FAR_Z, 1 );
		guScale( &matl, 0.25F, 0.25F, -0.25F );
		guMtxCatL( &matl, &cmat, &matt );
		guMtxCatL( &matt, &matp, &d->ProjectRight );
	}


	// clear depth buffer
	gDPSetDepthImage(gp++,OS_K0_TO_PHYSICAL(dfb));
	gDPSetCycleType(gp++,G_CYC_FILL);
	gDPSetColorImage( gp++, G_IM_FMT_RGBA, G_IM_SIZ_16b, currentProcess->ScreenX, dfb );
	gDPSetFillColor( gp++, (GPACK_ZDZ(G_MAXFBZ,0)<<16) | (GPACK_ZDZ(G_MAXFBZ,0)) );
	gDPFillRectangle( gp++, 0, 0, currentProcess->ScreenX-1, currentProcess->ScreenY-1 );

	gDPPipeSync(gp++);
	gDPSetColorImage(gp++,G_IM_FMT_RGBA, G_IM_SIZ_16b, currentProcess->ScreenX,cfb[draw_buffer]);
	if (SplitScreen)			// Clear middle strip of framebuffer...
	{
		gDPSetFillColor(gp++,GPACK_RGBA5551(0,0,0,255) << 16 | GPACK_RGBA5551(0,0,0,255));
		gDPFillRectangle( gp++,
											(currentProcess->ScreenX/2)-1, 0,
											(currentProcess->ScreenX/2)+1, currentProcess->ScreenY-1 );
	}


	// misc setup
	gDPPipeSync(gp++);
	gDPSetCycleType(gp++,G_CYC_1CYCLE);
	gDPSetColorDither(gp++,G_CD_MAGICSQ);
	gDPSetBlendColor( gp++, 0, 0, 0, 128 );
	gDPSetPrimColor( gp++, 0, 0, 255, 255, 255, 128 );


	if (SplitScreen)
	{
		gDPSetScissor(gp++,G_SC_NON_INTERLACE,0,0,(currentProcess->ScreenX/2)-1,(currentProcess->ScreenY));
	}

	// load matrices
	gSPMatrix(gp++,&d->projection,G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
//	gSPMatrix(gp++,&d->identity,G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);


	gSPSetGeometryMode( gp++, G_CULL_BACK | G_SHADE | G_SHADING_SMOOTH );
	gDPSetTextureLUT( gp++, G_TT_RGBA16 );
	gDPSetRenderMode( gp++, G_RM_OPA_SURF, G_RM_OPA_SURF2 );

	if (SplitScreen)
	{
		gp = DrawStadium( gp, draw_buffer, d );

		View_Set( &Views[1] );
		gDPPipeSync( gp++ );
		gSPViewport( gp++, &vpRight );
		gDPSetScissor(gp++,G_SC_NON_INTERLACE,(currentProcess->ScreenX/2)+1,0,currentProcess->ScreenX,currentProcess->ScreenY);
		gSPMatrix( gp++, &d->ProjectRight, G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH );
		gp = DrawStadium( gp, draw_buffer, d );

		View_Set( &Views[0] );
		gDPPipeSync( gp++ );
		gSPViewport( gp++, &vpLeft );
		gDPSetScissor(gp++,G_SC_NON_INTERLACE,0,0,(currentProcess->ScreenX/2)-1,(currentProcess->ScreenY));
		gSPMatrix(gp++,&d->ProjectLeft,G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
	}
	else
	{
		gp = DrawStadium( gp, draw_buffer, d );
	}


	gDPPipeSync( gp++ );

	if (1)
	{
		gSPLoadUcode(gp++,
			     (u64 *)gspF3DLX2_Rej_xbusTextStart,
			     (u64 *)gspF3DLX2_Rej_xbusDataStart);

		gSPSegment(gp++, 0, 0x0);	/* Physical address segment */
		gSPDisplayList(gp++, DLInitRDP);
		gSPDisplayList(gp++, DLInitRSP);
		if (SplitScreen)
		{
			gSPViewport( gp++, &vpLeft );
			gDPSetScissor(gp++,G_SC_NON_INTERLACE,0,0,(currentProcess->ScreenX/2)-1,(currentProcess->ScreenY));
		}
		else
		{
			gSPViewport(gp++,&vpFull);
			gp = SetScissor( gp );
		}

		gDPPipeSync(gp++);
		gSPPerspNormalize( gp++, perspnorm );
		gDPSetDepthImage(gp++,OS_K0_TO_PHYSICAL(dfb));
		gDPSetColorImage(gp++,G_IM_FMT_RGBA, G_IM_SIZ_16b, currentProcess->ScreenX,
					OS_K0_TO_PHYSICAL(cfb[draw_buffer]));
		gDPSetCycleType(gp++,G_CYC_1CYCLE);
		gSPSetGeometryMode(gp++,G_CULL_BACK);
		gDPSetColorDither(gp++,G_CD_MAGICSQ);
		gSPMatrix(gp++,&d->projection,G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
		gDPSetTextureLUT( gp++, G_TT_RGBA16 );
	}


// Draw players...
	gSPTexture( gp++, 0x8000, 0x8000, 0, G_TX_RENDERTILE, G_ON );
	gDPSetAlphaCompare( gp++, G_AC_NONE );



	gDPSetCombineMode( gp++, G_CC_DECALRGBA, G_CC_DECALRGBA );
	gSPSetGeometryMode(gp++,G_SHADE | G_SHADING_SMOOTH);

	gSPSetGeometryMode(gp++,G_ZBUFFER);
	gDPSetDepthSource(gp++,G_ZS_PIXEL);

	if (SplitScreen)
	{
		gp = BallGfx_Draw( gp, d, which, TRUE );

		View_Set( &Views[1] );
		gDPPipeSync( gp++ );
		gSPViewport( gp++, &vpRight );
		gDPSetScissor(gp++,G_SC_NON_INTERLACE,(currentProcess->ScreenX/2)+1,0,currentProcess->ScreenX,currentProcess->ScreenY);
		gSPMatrix( gp++, &d->ProjectRight, G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH );
		gp = BallGfx_Draw( gp, d, which, FALSE );

		View_Set( &Views[0] );
		gDPPipeSync( gp++ );
		gSPViewport( gp++, &vpLeft );
		gDPSetScissor(gp++,G_SC_NON_INTERLACE,0,0,(currentProcess->ScreenX/2)-1,(currentProcess->ScreenY));
		gSPMatrix(gp++,&d->ProjectLeft,G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
	}
	else
	{
		gp = BallGfx_Draw( gp, d, which, TRUE );
	}


	gDPPipeSync(gp++);
	gDPSetAlphaCompare( gp++, G_AC_NONE );
	gDPSetCombineMode( gp++, G_CC_MODULATERGBA, G_CC_MODULATERGBA );
	gSPSetGeometryMode(gp++,G_CULL_BACK | G_SHADE | G_SHADING_SMOOTH);
	gDPSetTextureLUT( gp++, G_TT_RGBA16 );

	gSPSetGeometryMode(gp++,G_ZBUFFER);
	gDPSetDepthSource(gp++,G_ZS_PIXEL);
	gDPSetRenderMode( gp++, G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2 );


	CalcPlayerDetails();
	VtxPtr = VtxLists[which];
	GfxPtr = GfxLists[which];

#if 1
	pi = PlayerInfos;

	for (i=0; i<10; i++)
	{
		if (pi->Active)
		{
			detail = pi->DetailLevel;

			switch (detail)
			{
				case DETAIL_LOW:
					AnimatePlayer( i, LowModel, DETAIL_LOW );
					break;

				case DETAIL_MEDIUM:
					AnimatePlayer( i, MediumModel, DETAIL_MEDIUM );
					break;

				case DETAIL_HIGH:
					AnimatePlayer( i, HighModel, DETAIL_HIGH );
					break;

				case DETAIL_EXTREME:
					AnimatePlayer( i, ExtremeModel, DETAIL_EXTREME );
					break;
			}

			if (pi->Display)
				GfxPtr = DrawPlayer( GfxPtr, i, which, detail );
		}

		pi++;
	}
#endif


	{
		u32
			vtx_size,
			gfx_size;
		char
			numstr[8],
			text[64];

		vtx_size = (u32)VtxPtr - (u32)VtxLists[which];
		gfx_size = (u32)GfxPtr - (u32)GfxLists[which];

		if (vtx_size > VTX_LIST_SIZE)
		{
			memset( numstr, 0, 8 );
			IntToDecStr( vtx_size, numstr );
			sprintf( text, "VtxList should be %s", numstr );
			FatalError( text, __FILE__, __LINE__ );
		}

		if (gfx_size > GFX_LIST_SIZE)
		{
			memset( numstr, 0, 8 );
			IntToDecStr( gfx_size, numstr );
			sprintf( text, "GfxList should be %s", numstr );
			FatalError( text, __FILE__, __LINE__ );
		}

		if (vtx_size > MaxVsize) MaxVsize = vtx_size;
		if (gfx_size > MaxGsize) MaxGsize = gfx_size;
	}


#if 1
	gSPMatrix( gp++, &d->identity, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );

	if (SplitScreen)
	{
		pi = PlayerInfos;
		for (i=0; i<10; i++)
		{
			if (pi->Active && pi->Display)
			{
				if (PlayerInClipVolume( &player[i] ))
				{
					gSPDisplayList( gp++, pi->GfxList );
				}
			}

			pi++;
		}

		View_Set( &Views[1] );
		gDPPipeSync( gp++ );
		gSPViewport( gp++, &vpRight );
		gDPSetScissor(gp++,G_SC_NON_INTERLACE,(currentProcess->ScreenX/2)+1,0,currentProcess->ScreenX,currentProcess->ScreenY);
		gSPMatrix( gp++, &d->ProjectRight, G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH );

		pi = PlayerInfos;
		for (i=0; i<10; i++)
		{
			if (pi->Active && pi->Display)
			{
				if (PlayerInClipVolume( &player[i] ))
				{
					gSPDisplayList( gp++, PlayerInfos[i].GfxList );
				}
			}

			pi++;
		}

		View_Set( &Views[0] );
		gDPPipeSync( gp++ );
		gSPViewport( gp++, &vpLeft );
		gDPSetScissor(gp++,G_SC_NON_INTERLACE,0,0,(currentProcess->ScreenX/2)-1,(currentProcess->ScreenY));
		gSPMatrix(gp++,&d->ProjectLeft,G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
	}
	else
	{
		for (i=0; i<10; i++)
		{
			pi = &PlayerInfos[SortList[i].Index];

			if (pi->Active && pi->Display)
			{
				gSPDisplayList( gp++, pi->GfxList );
			}
		}
	}
#endif


	if (ArcadeMode)
	{
		vector16
			*v;

		if (ShowTrails)
		{
			v = &player[ball_handler].nodeVector[NODE_LFOOT];
			Clouds_Add( v->x, v->y - 256, v->z );

			v = &player[ball_handler].nodeVector[NODE_RFOOT];
			Clouds_Add( v->x, v->y - 256, v->z );
		}

		if (BallTrail)
		{
			Clouds_Add( ball.flip_xco>>10,
									ball.flip_zco>>10,
									ball.flip_yco>>10 );
		}
	}

	gp = Clouds_Draw( gp, which, d );


	// Line drawing for Noah...
#ifdef NO_DEBUG_STUFF
	NumLines = 0;
#else
	{
		gDPPipeSync( gp++ );

		gSPLoadUcode( gp++,
									(u64 *)gspL3DEX2_xbusTextStart,
									(u64 *)gspL3DEX2_xbusDataStart);

		gSPSegment(gp++, 0, 0x0);	/* Physical address segment */
		gSPDisplayList(gp++, DLInitRDP);
		gSPDisplayList(gp++, DLInitRSP);
		gDPSetScissor(gp++,G_SC_NON_INTERLACE,0,0,(currentProcess->ScreenX),(currentProcess->ScreenY));
		if (SplitScreen)
		{
			gSPViewport( gp++, &vpLeft );
		}
		else
		{
			gSPViewport(gp++,&vpFull);
		}

		gDPPipeSync(gp++);
		gSPPerspNormalize( gp++, perspnorm );
		gDPSetDepthImage(gp++,OS_K0_TO_PHYSICAL(dfb));
		gDPSetColorImage(gp++,G_IM_FMT_RGBA, G_IM_SIZ_16b, currentProcess->ScreenX,
					OS_K0_TO_PHYSICAL(cfb[draw_buffer]));
		gDPSetCycleType(gp++,G_CYC_1CYCLE);
		gSPSetGeometryMode( gp++, G_SHADE | G_SHADING_SMOOTH );
		gDPSetColorDither(gp++,G_CD_MAGICSQ);
		gSPMatrix(gp++,&d->projection,G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
		gSPMatrix( gp++, &d->identity, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );
		gDPSetTextureLUT( gp++, G_TT_NONE );

		gp = DrawLines( gp );
	}

	gDPPipeSync( gp++ );

	if (1)
	{
		gSPLoadUcode(gp++,
			     (u64 *)gspF3DLX2_Rej_xbusTextStart,
			     (u64 *)gspF3DLX2_Rej_xbusDataStart);

		gSPSegment(gp++, 0, 0x0);	/* Physical address segment */
		gSPDisplayList(gp++, DLInitRDP);
		gSPDisplayList(gp++, DLInitRSP);
		if (SplitScreen)
		{
			gSPViewport( gp++, &vpLeft );
			gDPSetScissor(gp++,G_SC_NON_INTERLACE,0,0,(currentProcess->ScreenX/2)-1,(currentProcess->ScreenY));
		}
		else
		{
			gSPViewport(gp++,&vpFull);
			gp = SetScissor( gp );
		}

		gDPPipeSync(gp++);
		gSPPerspNormalize( gp++, perspnorm );
		gDPSetDepthImage(gp++,OS_K0_TO_PHYSICAL(dfb));
		gDPSetColorImage(gp++,G_IM_FMT_RGBA, G_IM_SIZ_16b, currentProcess->ScreenX,
					OS_K0_TO_PHYSICAL(cfb[draw_buffer]));
		gDPSetCycleType(gp++,G_CYC_1CYCLE);
		gSPSetGeometryMode(gp++,G_CULL_BACK);
		gDPSetColorDither(gp++,G_CD_MAGICSQ);
		gSPMatrix(gp++,&d->projection,G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
		gDPSetTextureLUT( gp++, G_TT_RGBA16 );
	}
#endif


	if (!AttractMode && !C3PT)
	{
		if (PlayerInfos[ball_handler].Display)
			gp = Caption_Draw( gp, d, bhp );
	}
	gDPPipeSync(gp++);
	gDPSetTexturePersp( gp++, G_TP_PERSP );


	gSPClearGeometryMode( gp++, G_CULL_BACK );
	gDPSetAlphaCompare( gp++, G_AC_NONE );

	if (SplitScreen)
	{
		CalcNet( 0, BASKET_Y>>10 );
		gp = DrawNet( gp, 0, &NetVerts[which][0][0], d );

		gDPSetRenderMode( gp++, G_RM_ZB_CLD_SURF, G_RM_ZB_CLD_SURF2 );
//gSPClipRatio( gp++, FRUSTRATIO_6 );
		gSPMatrix( gp++, &d->identity, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );
		gp = Post_DrawGlass( gp, 0, which, d );

		View_Set( &Views[1] );
		gDPPipeSync( gp++ );
		gSPViewport( gp++, &vpRight );
		gDPSetScissor(gp++,G_SC_NON_INTERLACE,(currentProcess->ScreenX/2)+1,0,currentProcess->ScreenX,currentProcess->ScreenY);
		gSPMatrix( gp++, &d->ProjectRight, G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH );
		CalcNet( 1, -BASKET_Y>>10 );
		gp = DrawNet( gp, 1, &NetVerts[which][1][0], d );

		gDPSetRenderMode( gp++, G_RM_ZB_CLD_SURF, G_RM_ZB_CLD_SURF2 );
//gSPClipRatio( gp++, FRUSTRATIO_6 );
		gSPMatrix( gp++, &d->identity, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );
		gp = Post_DrawGlass( gp, 1, which, d );

		View_Set( &Views[0] );
		gDPPipeSync( gp++ );
		gSPViewport( gp++, &vpLeft );
		gDPSetScissor(gp++,G_SC_NON_INTERLACE,0,0,(currentProcess->ScreenX/2)-1,(currentProcess->ScreenY));
		gSPMatrix(gp++,&d->ProjectLeft,G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
	}
	else
	{
		if (ShowFreeThrowNet)
		{
			if (whichEnd > 0)
			{
				CalcNet( 0, BASKET_Y>>10 );
				gp = DrawNet( gp, 0, &NetVerts[which][0][0], d );
				gp = DrawNet( gp, 2, &NetVerts[which][1][0], d );
			}
			else
			{
				CalcNet( 1, -BASKET_Y>>10 );
				gp = DrawNet( gp, 1, &NetVerts[which][1][0], d );
				gp = DrawNet( gp, 3, &NetVerts[which][0][0], d );
			}

			ShowFreeThrowNet = FALSE;
		}
		else
		{
			CalcNet( 0, BASKET_Y>>10 );
			gp = DrawNet( gp, 0, &NetVerts[which][0][0], d );

			CalcNet( 1, -BASKET_Y>>10 );
			gp = DrawNet( gp, 1, &NetVerts[which][1][0], d );
		}

		gDPSetRenderMode( gp++, G_RM_ZB_CLD_SURF, G_RM_ZB_CLD_SURF2 );
//gSPClipRatio( gp++, FRUSTRATIO_6 );
		gSPMatrix( gp++, &d->identity, G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );
		gp = Post_DrawGlass( gp, 0, which, d );
		gp = Post_DrawGlass( gp, 1, which, d );
	}


	gDPPipeSync( gp++ );
	gDPSetRenderMode( gp++, G_RM_OPA_SURF, G_RM_OPA_SURF2 );
	gSPSetGeometryMode( gp++, G_SHADE | G_SHADING_SMOOTH );
	gSPClearGeometryMode( gp++, G_ZBUFFER );

	gp = DrawPoints( gp, d );


gDPPipeSync(gp++);
gSPViewport( gp++, &vpFull );
gDPSetScissor(gp++,G_SC_NON_INTERLACE,0,0,(currentProcess->ScreenX),(currentProcess->ScreenY));

#if 1
	gDPPipeSync(gp++);
	gDPSetCycleType(gp++,G_CYC_1CYCLE);
	gDPSetRenderMode( gp++, G_RM_OPA_SURF, G_RM_OPA_SURF2 );
	gDPSetTexturePersp( gp++, G_TP_NONE );
	gDPSetTextureFilter( gp++, G_TF_POINT );
	gDPSetAlphaCompare( gp++, G_AC_NONE );

	gp = (Gfx *)FreeThrowRender( gp, d );
	gp = OverGfx_Draw( gp, d );

	gDPPipeSync( gp++ );
//	gDPSetAlphaCompare( gp++, G_AC_THRESHOLD );
//	gDPSetBlendColor( gp++, 0, 0, 0, 128 );
	gDPSetCombineMode( gp++, G_CC_DECALRGBA, G_CC_DECALRGBA );
	gDPSetTextureLUT( gp++, G_TT_NONE );
	gDPLoadTextureBlock( gp++, Font, G_IM_FMT_I, G_IM_SIZ_8b, 128, 32, 0,
											 G_TX_WRAP, G_TX_WRAP, 7, 5, G_TX_NOLOD, G_TX_NOLOD );

 #ifndef MIKE

#if 1

#if 0
	for (i=0; i<10; i++)
	{
		if (bhp == &player[i])
			gp = DrawChar( gp, 'B', 2+(i*2), 0 );

		gp = DrawChar( gp, '0'+PlayerInfos[i].DetailLevel, 2+(i*2), 1 );
	}
#endif

	{
#if 0
		gp = Track_Show( gp, DrawNumber );
		gp = DrawNumber( gp, Capacity, 4, 8 );
		gp = DrawNumber( gp, VolLevel, 4, 9 );
		gp = DrawNumber( gp, ZZZ, 4, 2 );
		gp = DrawNumber( gp, sl___gCurMemFree*16/1024, 4, 2 );
		gp = DrawNumber( gp, AudioOverruns, 4, 3 );
		gp = DrawNumber( gp, (MaxVsize+1023)/1024, 4, 10 );
		gp = DrawNumber( gp, (MaxGsize+1023)/1024, 4, 11 );
#endif
#if 0
		gp = DrawNumber( gp, AttractFrame>>8, 4, 1 );
		gp = DrawNumber( gp, camTargetNo, 4, 2 );
		gp = DrawNumber( gp, camZoomUser, 4, 3 );

		if (camSpecialMode != -1)
		{
			static const char
				*names[] =
				{
					"DribbleI",
					"Backboard",
					"Backboard2",
					"Basket",
					"BaseLine",
					"BaseLine2",
					"Cheer",
					"Overhead",
					"Overhead2",
					"SideHoop",
					"InYourFace",
					"Shoulder1",
					"Shoulder2",
					"Shoulder3",
					"FreeThrow",
					"UpChest",
					"DribbleK",
					"FaceVH",
					"Intro4",
					"-",
					"FaceAI",
					"-",
					"-",
					"Face1",
				};

			gp = DrawString( gp, names[camSpecialMode], 4, 4 );
			gp = DrawString( gp, "Special", 16, 4 );
		}
		else
		{
			gp = DrawString( gp, camModeNames[camMode], 4, 4 );
			gp = DrawString( gp, "Mode", 16, 4 );
		}
#endif

	}
#endif

 #endif	

#if defined(DEBUG)
	gp=DrawString(gp,"Scoring",16,2);
	if (SR_Team!=-1)
	{
		gp=DrawNumber(gp,SR_Points,24,2);
		gp=DrawString(gp,pcl__gMatch->apcl_mTeamRom[SR_Team]->pcz__tName,28,2);
	}

	#if defined(SHOT_TRACKING) && defined(DEBUG)
		gp=DrawString(gp,print_shot(),4,4);
	#endif
#endif

#if defined(DEBUG) && 1
	if (C3PT)
	{
		gp=DrawNumber(gp,player[0].Score3,4,10);
		gp=DrawNumber(gp,player[0].Rack3*5+player[0].Balls3,4,11);
	}
#endif

#if defined(DEBUG)
		if (fast_break_flag)
			gp=DrawString(gp,"Fast Break",4,6);

		if (bhp && !C3PT)
		{
			int	anim=bhp->top_anim;

			gp=DrawString(gp,main_names[bhp->mr_no],2,0);
			gp=DrawString(gp,a_names[abs(anim)],2,1);
			if (anim<0)
				gp=DrawString(gp,"-",1,1);
			gp=DrawHexNum(gp,bhp->top_rate,2,2);
		}

		gp=DrawNumber(gp,time_rate*100/256,8,2);
#endif

#if defined(DEBUG) || defined(LOGGING)

	#if defined(NOAH)
		if (Announcer==Chick)
			gp=DrawString(gp,"Chick",20,1);
		else
			gp=DrawString(gp,"Stu",20,1);
	#endif

	#if defined(NOAH) && 0

		for (i=0; i<10; i++)
		{
			char	num[2]="0";
			int		x,y;
			playert	*plyr=&player[i];

			x=plyr->target_x;
			y=plyr->target_y;
			if (plyr->xco!=plyr->flip_xco)
			{
				x=-x;
				y=-y;
			}
			num[0]='0'+i;

			gp=DrawTextAt3D(gp,d,num,x>>10,0,y>>10);
		}

		for (i=0; i<10; i++)
			if (player[i].control==1)
				gp=DrawNumber(gp,player[i].key_ctr,30,1);

		if (dunk_flag)
			gp=DrawString(gp,"Dunk",4,11);
		gp=DrawNumber(gp,shoot_stage,4,10);

		{
			SW	a;

			a=bhp->angle-bhp->basket_angle;
			gp=DrawNumber(gp,a,20,15);
		}
		for (i=0; i<10; i++)
		{
			if (player[i].main_routine==cut_to_basket)
			{
				char msg[]="0: Cut";

				msg[0]='0'+i;
				gp=DrawString(gp,msg,16,4+i);
			}
			else if (player[i].main_routine==set_pick)
			{
				char msg[]="0: Pick";

				msg[0]='0'+i;
				gp=DrawString(gp,msg,16,4+i);
			}
		}

	#endif


#endif

#if defined(DEBUG) && BIGNOAH
	if (!C3PT && InfoFlag)
	{
		playert *plyr;
		int		row;

		if (bhp && pt3_detect(bhp))
			gp=DrawString(gp,"3 Point Land",20,0);

		if (replay_flag && camReplayTargetNo>=0 && camReplayTargetNo<10)
		//if (camReplayTargetNo>=0 && camReplayTargetNo<10)
		{
			plyr=&player[camReplayTargetNo];

			row=5;

			gp=DrawString(gp,idString,4,row++);
			gp=DrawString(gp,mrString,4,row++);
			gp=DrawString(gp,tpString,4,row++);
			gp=DrawString(gp,lgString,4,row++);

			if (plyr->leg_anim<0)
				gp=DrawString(gp,"-",3,row);
			gp=DrawString(gp,l_names[abs(plyr->leg_anim)],4,row++);
			gp=DrawNumber(gp,plyr->leg_frame/256,6,row);
			gp=DrawNumber(gp,plyr->leg_frame&0xff,10,row);
			gp = DrawNumber( gp, plyr->ikBlend, 24, row );
			gp = DrawNumber( gp, plyr->ikFrameNo, 32, row );
			gp=DrawNumber(gp,plyr->leg_blend_ctr,14,row++);
			if (plyr->last_leg_anim<0)
				gp=DrawString(gp,"-",5,row);
			gp=DrawString(gp,l_names[abs(plyr->last_leg_anim)],6,row++);
			gp=DrawNumber(gp,plyr->last_leg_frame/256,6,row);
			gp=DrawNumber(gp,plyr->last_leg_frame&0xff,10,row++);

			if (plyr->top_anim<0)
				gp=DrawString(gp,"-",3,row);
			gp=DrawString(gp,a_names[abs(plyr->top_anim)],4,row++);
			gp=DrawNumber(gp,plyr->top_frame/256,6,row);
			gp=DrawNumber(gp,plyr->top_frame&0xff,10,row);
			gp=DrawNumber(gp,plyr->top_blend_ctr,14,row++);
			if (plyr->last_top_anim<0)
				gp=DrawString(gp,"-",5,row);
			gp=DrawString(gp,a_names[abs(plyr->last_top_anim)],6,row++);
			gp=DrawNumber(gp,plyr->last_top_frame/256,6,row);
			gp=DrawNumber(gp,plyr->last_top_frame&0xff,10,row++);

			gp=DrawNumber(gp,plyr->speed,4,row);
		}
	}
#endif

	

#if defined(ML) || defined(NOAH)
	{
		int row = 1;
		gp = DrawString(gp, "base",40, row);
		gp = DrawNumber(gp, status_var5, 50, row++);
		gp = DrawString(gp, "speed",40, row);
		gp = DrawNumber(gp, status_var1, 50, row++);
		gp = DrawString(gp, "turn",40, row);
		gp = DrawNumber(gp, status_var2, 50, row++);
		gp = DrawString(gp, "block",40, row);
		gp = DrawNumber(gp, status_var3, 50, row++);
		gp = DrawString(gp, "release",40, row);
		gp = DrawNumber(gp, status_var4, 50, row++);
		gp = DrawString(gp, "final",40, row);
		gp = DrawNumber(gp, status_var6, 50, row++);
		gp = DrawString(gp, "adjusted", 40, row);
		gp = DrawNumber(gp, status_var8, 50, row++);
		gp = DrawNumber(gp, status_var7, 50, row++);
	}
#endif



#endif

	gDPPipeSync(gp++);


	// write back gfx ptr
	(*gl)=gp;
}

/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/

#define REDO_HEAD 1
#define REDO_BODY 2


global PlyrRomData_T
	ShellRomData;


static char
	ShellName[16] = "";

static SCamera
	ShellOldCam,
	ShellNewCam,
	ShellCamera;

static u16
	ShellInterp,
	ShellFocus;


void PlayGfx_ShellFocus( u32 focus )
{
	if (focus != ShellFocus)
	{
		Cam_Interpolate( &ShellOldCam, &ShellNewCam, ShellInterp );
		Cam_Save( &ShellOldCam );

		ShellInterp = 0;
		ShellFocus = focus;
	}
}


void PlayGfx_ShellName( char *name )
{
	strcpy( ShellName, name );
	ShellRedo |= REDO_BODY;
}


void PlayGfx_ShellScale( s32 scale )
{
	player[0].scale = scale;
}


void PlayGfx_ShellHead( s32 head, s32 hair )
{
	PlyrCustomInfo_T
		*custom;

	custom = (PlyrCustomInfo_T *)&ShellRomData.cl___pStatNBA;
	custom->ub___pCustomHeadType = head;
	custom->ub___pCustomHairType = hair;

	ShellRedo |= REDO_HEAD;
}


void PlayGfx_ShellArms( u8 arms )
{
	ShellRomData.ub___pAcc0 &= ~PLYR_HAS_BAPAD;
	ShellRomData.ub___pAcc0 |= arms;

	ShellRedo |= REDO_BODY;
}


void PlayGfx_ShellWrists( u8 wrists )
{
	ShellRomData.ub___pAcc0 &= ~PLYR_HAS_BWPAD;
	ShellRomData.ub___pAcc0 |= (wrists << 2);

	ShellRedo |= REDO_BODY;
}


void PlayGfx_ShellKnees( u8 knees )
{
	ShellRomData.ub___pAcc0 &= ~PLYR_HAS_BKPAD;
	ShellRomData.ub___pAcc0 |= (knees << 4);

	ShellRedo |= REDO_BODY;
}


void PlayGfx_ShellSocks( u8 socks )
{
	ShellRomData.ub___pAcc0 &= ~PLYR_HAS_SOCKS;
	ShellRomData.ub___pAcc0 |= (socks << 6);

	ShellRedo |= REDO_BODY;
}


void PlayGfx_ShellNumber( u8 number )
{
	ShellRomData.ub___pNum = number;

	ShellRedo |= REDO_BODY;
}


void PlayGfx_ShellBody( u8 body )
{
	SPlayerInfo
		*pi = &PlayerInfos[0];
	s32
		i,
		prev;

	prev = (player[0].plyrROM->ub___pAcc1 >> 6) & 3;
	if (body == prev)
		return;

	player[0].plyrROM->ub___pAcc1 &= ~(3 << 6);
	player[0].plyrROM->ub___pAcc1 |= (body << 6);
	ShellRedo |= REDO_BODY;


	for (i=0; i<MAX_BONES; i++)
	{
		switch (i)
		{
			case NODE_HEAD:
				pi->NodeLists[DETAIL_EXTREME][i] = NULL;
				break;

			case NODE_LHAND:
			case NODE_RHAND:
				pi->NodeLists[DETAIL_EXTREME][i] = GetNode( ExtremeModel, i, (body*2) );
				break;

			default:
				pi->NodeLists[DETAIL_EXTREME][i] = GetNode( ExtremeModel, i, body );
		}
	}


	GetMesh( ExtremeModel, MESH_BODY,  body,     &pi->MeshLists[DETAIL_EXTREME][MESH_BODY] );
	GetMesh( ExtremeModel, MESH_LHAND, (body*2), &pi->MeshLists[DETAIL_EXTREME][MESH_LHAND] );
	GetMesh( ExtremeModel, MESH_RHAND, (body*2), &pi->MeshLists[DETAIL_EXTREME][MESH_RHAND] );
}


void PlayGfx_ShellInit()
{
	SPlayerInfo
		*pi;
	SMaterial
		*mtl;
	FH
		file;
	u8
		*ptr,
		*tex;
	u16
		psize,
		lsize,
		hsize;
	s32
		i,
		j,
		off,
		size,
		team,
		offset;

	AttractMode = FALSE;
	UseOtherUniform = FALSE;
	C3PT = FALSE;


	ShellNewCam.Yaw = 0;
	ShellNewCam.Pitch = 0;
	ShellNewCam.x = 0;
	ShellNewCam.y = 0;
	ShellNewCam.z = 5*1024;

	ShellOldCam = ShellNewCam;
	ShellInterp = (1 << 8);
	ShellFocus = PG_FOCUS_ALL;

	ShellRedo = REDO_HEAD | REDO_BODY;


	player[0].scale = 256;


	SetupBoneOffs();

	VtxLists[0] = MemMalloc( VTX_LIST_SIZE, MEM_ANY );
	VtxLists[1] = MemMalloc( VTX_LIST_SIZE, MEM_ANY );

	GfxLists[0] = MemMalloc( GFX_LIST_SIZE, MEM_ANY );
	GfxLists[1] = MemMalloc( GFX_LIST_SIZE, MEM_ANY );

	LowModel			= AllocAndLoadFile( "LOW.MDL",			MEM_ANY );
	MediumModel		= AllocAndLoadFile( "MEDIUM.MDL",		MEM_ANY );
	HighModel			= AllocAndLoadFile( "HIGH.MDL",			MEM_ANY );
	ExtremeModel	= AllocAndLoadFile( "extreme.mdl",	MEM_ANY );

	ExtremeMtls = AllocAndLoadFile( "bighead.mtl", MEM_ANY );
	FixUpMatList( ExtremeMtls, 4 );

	BigHeadModel = AllocAndLoadFile( "bighead.mdl", MEM_ANY );
	FaceAnims = AllocAndLoadFile( "Face.ANM", MEM_ANY );


	off = 0;
	for (j=0; j<MAX_BONES; j++)
	{
		VertStarts[DETAIL_EXTREME][j] = off;

		i = *(s32 *)&ExtremeModel[5*4];
		ptr = &ExtremeModel[i + (j * 16)];

		off += ptr[1];
	}


	off = 0;
	for (j=0; j<HEAD_NODES; j++)
	{
		HeadStarts[j] = off;

		i = *(s32 *)&BigHeadModel[5*4];
		ptr = &BigHeadModel[i + (j * 16)];

		off += ptr[1];
	}


//	memset( &ShellRomData, 0, sizeof( PlyrRomData_T ) );
//	ShellRomData.uw___pIndx = si___gPlyrRomCnt;
	wordcpy( &ShellRomData, gCustomPlayerPtr, sizeof( PlyrRomData_T ) );
	ShellRomData.pcz__pSName = ShellName;
	player[0].plyrROM = &ShellRomData;

	pi = &PlayerInfos[0];
	pi->Active = TRUE;
	InitPlayer( 0 );


	AdjustExtremeModel();
}


void PlayGfx_ShellDeinit()
{
	DeinitPlayer( 0 );

	MemFree( FaceAnims );
	MemFree( BigHeadModel );
	MemFree( ExtremeMtls );

	MemFree( ExtremeModel );
	MemFree( HighModel );
	MemFree( MediumModel );
	MemFree( LowModel );

	MemFree( GfxLists[1] );
	MemFree( GfxLists[0] );

	MemFree( VtxLists[1] );
	MemFree( VtxLists[0] );
}


static void ShellAnimate( const s32 pnum, const u8 *model, const s32 detail )
{
	playert
		*plyr;
	s16
		x,
		y,
		z,
		angles[3];
	Quat
		qBone,
		*qParent;
	vector16
		*v,
		*points,
		vector;
	s32
		i,
		p,
		body,
		count;
	int
		lf,
		rf;
	u8
		*ptr;

	plyr = &player[0];


	angles[0] = 0;
	angles[1] = 0;
	angles[2] = 0;

	AnglesToQuatPRH(&plyr->rootQuat, angles);
	QuatToMatrix(&plyr->rootMatrix, &plyr->rootQuat);

	for (i=0; i<MAX_BONES; i++)
	{
		p = ParentList[i];

		if (p < 0)			// Hips/Pelvis
		{
			qParent = &plyr->rootQuat;

			plyr->nodeVector[i].x=0;
			plyr->nodeVector[i].y=plyr->scale;
			plyr->nodeVector[i].z=0;

			Vec16MulMat(plyr->nodeVector+i, &plyr->rootMatrix, plyr->nodeVector+i);
		}
		else
		{
			qParent = plyr->skelQuat+p;

			Vec16MulMat(&BoneOffs[i], plyr->skelMatrix+p, plyr->nodeVector+i);

			plyr->nodeVector[i].x += plyr->nodeVector[p].x;
			plyr->nodeVector[i].y += plyr->nodeVector[p].y;
			plyr->nodeVector[i].z += plyr->nodeVector[p].z;
		}

		if (i == NODE_CHEST)	qParent = &plyr->rootQuat;


		if (i == NODE_HEAD)
		{
			s16
				angles[3] = { 0, 0, ShellCamera.x };

			AnglesToQuatPRH( &qBone, angles );
			QuatMul( plyr->skelQuat+i, qParent, &qBone );
		}
		else
//			memcpy( plyr->skelQuat+i, qParent, sizeof( Quat ) );
			plyr->skelQuat[i] = *qParent;

		QuatToMatrix( plyr->skelMatrix+i, plyr->skelQuat+i);

		plyr->skelMatrix[i].xX = (plyr->skelMatrix[i].xX * plyr->scale) >> 8;
		plyr->skelMatrix[i].xY = (plyr->skelMatrix[i].xY * plyr->scale) >> 8;
		plyr->skelMatrix[i].xZ = (plyr->skelMatrix[i].xZ * plyr->scale) >> 8;
		plyr->skelMatrix[i].yX = (plyr->skelMatrix[i].yX * plyr->scale) >> 8;
		plyr->skelMatrix[i].yY = (plyr->skelMatrix[i].yY * plyr->scale) >> 8;
		plyr->skelMatrix[i].yZ = (plyr->skelMatrix[i].yZ * plyr->scale) >> 8;
		plyr->skelMatrix[i].zX = (plyr->skelMatrix[i].zX * plyr->scale) >> 8;
		plyr->skelMatrix[i].zY = (plyr->skelMatrix[i].zY * plyr->scale) >> 8;
		plyr->skelMatrix[i].zZ = (plyr->skelMatrix[i].zZ * plyr->scale) >> 8;


		if (detail == DETAIL_EXTREME && i == NODE_HEAD)
		{
			AnimateHead( pnum, PlayerInfos[pnum].HeadModel );

			count = 100;
			v = &WorkVerts[VertStarts[DETAIL_EXTREME][NODE_HEAD]];
			points = v;
		}
		else
		{
			ptr = PlayerInfos[pnum].NodeLists[detail][i];
			if (ptr)
			{
				count = *(s32 *)ptr;
				points = (vector16 *)&ptr[4];
				v = &WorkVerts[VertStarts[detail][i]];
			}
			else
				count = 0;
		}

#if 0
		while (count)
		{
			x = points->x;
			y = points->y;
			z = points->z;

			v->x = (((x * plyr->skelMatrix[i].xX) +
							 (y * plyr->skelMatrix[i].yX) +
							 (z * plyr->skelMatrix[i].zX)) >> 14)
									+ plyr->nodeVector[i].x;

			v->y = (((x * plyr->skelMatrix[i].xY) +
							 (y * plyr->skelMatrix[i].yY) +
							 (z * plyr->skelMatrix[i].zY)) >> 14)
									+ plyr->nodeVector[i].y;

			v->z = (((x * plyr->skelMatrix[i].xZ) +
							 (y * plyr->skelMatrix[i].yZ) +
							 (z * plyr->skelMatrix[i].zZ)) >> 14)
									+ plyr->nodeVector[i].z;

			v++;
			points++;
			count--;
		}
#else
		if (count)
			TransformVectors( points, count, &plyr->skelMatrix[i], &plyr->nodeVector[i], v );
#endif
	}
}


static void ShellDraw( Gfx *gp, const s32 pnum, const s32 db, const s32 detail )
{
	SPlayerInfo
		*pi;
	VertexN64
		*v;
	SMaterial
		*mtl;
	u32
		*vptr;
	s32
		i,
		draw = TRUE;
	u16
		cmd,
		cnt,
		*ptr,
		*cptr;

	pi = &PlayerInfos[pnum];

	v = VtxLists[db];
	pi->GfxList = gp;


	for (i=0; i<MESH_COUNT; i++)
	{
		vptr = pi->MeshLists[detail][i].TexCoords;
		cptr = pi->MeshLists[detail][i].Commands;
if (vptr)
{
		cmd = -1;
		while (cmd != CMD_END)
		{
			cmd = cptr[0];
			cnt = cptr[1];
			cptr += 2;

			switch (cmd)
			{
				case CMD_TEXTURE:
					switch (detail)
					{
						case DETAIL_LOW:
							mtl = &pi->MtlLists[TEXSET_LOW][cnt];
							break;

						case DETAIL_MEDIUM:
							mtl = &pi->MtlLists[TEXSET_HIGH][cnt];
							break;

						case DETAIL_HIGH:
							mtl = &pi->MtlLists[TEXSET_HIGH][cnt];
							break;

						case DETAIL_EXTREME:
							mtl = &pi->MtlLists[TEXSET_EXTREME][cnt];
							break;
					}

					if (mtl->Texture)
					{
						gDPLoadTextureBlock( gp++,
																 mtl->Texture,
																 G_IM_FMT_CI,
																 G_IM_SIZ_8b,
																 mtl->Width,
																 mtl->Height,
																 0,
																 mtl->Flags.Xflags,
																 mtl->Flags.Yflags,
																 mtl->Flags.Xmask,
																 mtl->Flags.Ymask,
																 G_TX_NOLOD,
																 G_TX_NOLOD );
					}

					if (mtl->Palette)
					{
						gDPLoadTLUT_pal256( gp++, mtl->Palette );
					}
					break;

				case CMD_VERTICES:
					draw = TRUE;
					gSPVertex( gp++, v, cnt, 0 );

					while (cnt > 0)
					{
						*(u64 *)&v->x = *(u64 *)&WorkVerts[*cptr++];
						*(u32 *)&v->u = *vptr++;
						*(s32 *)&v->r = 0xe6e6e6ff;

						v++;
						cnt--;
					}
					break;

				case CMD_FACES:
					while (cnt >= 2)
					{
						if (draw)
						{
							gSP2Triangles( gp++,
														 cptr[0], cptr[1], cptr[2], 0,
														 cptr[3], cptr[4], cptr[5], 0 );
						}
						cptr += 2*3;
						cnt -= 2;
					}
					if (cnt)
					{
						if (draw)
						{
							gSP1Triangle( gp++, cptr[0], cptr[1], cptr[2], 0 );
						}
						cptr += 3;
					}
					break;

#if 0
				case CMD_ALPHA:
					gDPPipeSync( gp++ );
					gSPClearGeometryMode( gp++, G_CULL_BACK );
					gSPSetGeometryMode( gp++, G_CULL_FRONT );
					gDPSetRenderMode( gp++, G_RM_ZB_XLU_SURF, G_RM_ZB_XLU_SURF2 );
					break;

				case CMD_OPAQUE:
					gDPPipeSync( gp++ );
					gSPClearGeometryMode( gp++, G_CULL_FRONT );
					gSPSetGeometryMode( gp++, G_CULL_BACK );
					gDPSetRenderMode( gp++, G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2 );
					break;
#endif

				case CMD_NEGVERTS:
					draw = FALSE;
					gSPVertex( gp++, v, cnt, 0 );

					while (cnt > 0)
					{
						*(u64 *)&v->x = *(u64 *)&WorkVerts[*cptr++];
						*(u32 *)&v->u = *vptr++;
						*(s32 *)&v->r = 0xe6e6e6ff;

						v++;
						cnt--;
					}
					break;

				case CMD_ZTEST:
					gDPSetRenderMode( gp++, G_RM_ZTEST, G_RM_ZTEST2 );
					break;

				case CMD_ZUPDATE:
					gDPPipeSync( gp++ );
					gDPSetRenderMode( gp++, G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2 );
					break;
			}
		}
}
	}

	gSPEndDisplayList( gp++ );
}

#define CAM_FACE_RANGE	(5*1024)	//8192 - sorry, Phil, I can't stand 180 degrees - mjk

Gfx *PlayGfx_ShellDraw( Gfx *gp, s32 db, dyn *d )
{
	static s32
		dir = 128;
	SCamera
		*camera;
	matrix
		cam;
	Mtx
		matp,
		matl;
	matrix
		matz;
	u16
		perspnorm;

	if (ShellRedo)
	{
		Gfx_Wait();

		if (ShellRedo & REDO_HEAD)
		{
			FreeHead( &PlayerInfos[0] );
			LoadSkin( &player[0], &PlayerInfos[0] );
			LoadHead( &player[0], &PlayerInfos[0] );
		}

		if (ShellRedo & REDO_BODY)
		{
			LoadBodyTexs( &player[0], &PlayerInfos[0] );

			DeinitAccessories( &PlayerInfos[0] );
			InitAccessories( &player[0], &PlayerInfos[0] );
		}

		ShellRedo = 0;
	}


	ShellAnimate( 0, ExtremeModel, DETAIL_EXTREME );
	ShellDraw( GfxLists[db], 0, db, DETAIL_EXTREME );


	vpFull.vp.vscale[0] = (176/2) << 2;
	vpFull.vp.vscale[1] = (208/2) << 2;
	vpFull.vp.vtrans[0] = (336+(176/2)-8) << 2;
	vpFull.vp.vtrans[1] = (32+(208/2)) << 2;


	camera = &ShellNewCam;

	switch (ShellFocus)
	{
		case PG_FOCUS_ALL:
			camera->Yaw += 256;
			if (camera->Yaw > 32768)
				camera->Yaw -= 65536;
			camera->x = 0;
			camera->y = 0;
			camera->z = 5*1024;
			break;

		case PG_FOCUS_FACE:
			if (camera->Yaw < -CAM_FACE_RANGE)
				camera->Yaw += 192;
			else if (camera->Yaw > +CAM_FACE_RANGE)
				camera->Yaw -= 192;
			else
			{
				camera->Yaw += dir;
				if (camera->Yaw < -CAM_FACE_RANGE || camera->Yaw > +CAM_FACE_RANGE)
					dir = -dir;
			}

			camera->x = (camera->Yaw * 3) >> 2;
			if (camera->x < (-CAM_FACE_RANGE*3)/2) camera->x = (-CAM_FACE_RANGE*3)/2;
			if (camera->x > (+CAM_FACE_RANGE*3)/2) camera->x = (+CAM_FACE_RANGE*3)/2;

			camera->y = -player[0].scale * 4;
			camera->z = 800 + ((player[0].scale - 171) * 5);
			break;

		case PG_FOCUS_BACK:
			camera->Yaw = 32768;
			camera->x = 0;
			camera->y = -600;
			camera->z = 2*1024;
			break;
	}

	Cam_Interpolate( &ShellOldCam, &ShellNewCam, ShellInterp );

	if (ShellInterp < (1 << 8))
		ShellInterp += 4;

	Cam_Save( &ShellCamera );


	MakeMatPRH( &cam, 0, 0, ShellCamera.Yaw );
	MatToN64( &cam, &d->viewing );
	SetTransN64( &d->viewing, 0, ShellCamera.y, ShellCamera.z );

	guPerspective( &matp, &perspnorm, FOV, 0.52F, NEAR_Z, 2048/*FAR_Z*/, 1 );
	guScale( &matl, 0.20F, 0.20F, -0.20F );
	guMtxCatL( &matl, &matp, &d->projection );

	gSPPerspNormalize( gp++, perspnorm );


	gDPPipeSync( gp++ );

	{
		gSPLoadUcode(gp++,
			     (u64 *)gspF3DLX2_Rej_xbusTextStart,
			     (u64 *)gspF3DLX2_Rej_xbusDataStart);

		gSPSegment(gp++, 0, 0x0);	/* Physical address segment */
		gSPDisplayList(gp++, DLInitRDP);
		gSPDisplayList(gp++, DLInitRSP);
		gSPViewport(gp++,&vpFull);

	gDPSetScissor( gp++, G_SC_NON_INTERLACE, 336, 32, 512, 200 );

		gDPPipeSync(gp++);
		gSPPerspNormalize( gp++, perspnorm );
		gDPSetDepthImage(gp++,OS_K0_TO_PHYSICAL(dfb));
		gDPSetColorImage(gp++,G_IM_FMT_RGBA, G_IM_SIZ_16b, currentProcess->ScreenX,OS_K0_TO_PHYSICAL(cfb[db]));
		gDPSetCycleType(gp++,G_CYC_1CYCLE);
		gSPSetGeometryMode(gp++,G_CULL_BACK | G_SHADE | G_SHADING_SMOOTH | G_ZBUFFER);
		gDPSetColorDither(gp++,G_CD_MAGICSQ);
		gSPMatrix(gp++,&d->projection,G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH);
		gSPMatrix(gp++,&d->viewing,G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH);
	}

	gDPSetAlphaCompare( gp++, G_AC_NONE );
	gDPSetDepthSource(gp++,G_ZS_PIXEL);
	gDPSetRenderMode( gp++, G_RM_ZB_OPA_SURF, G_RM_ZB_OPA_SURF2 );
	gDPSetCombineMode( gp++, G_CC_MODULATERGBA, G_CC_MODULATERGBA );
	gSPTexture( gp++, 0x8000, 0x8000, 0, G_TX_RENDERTILE, G_ON );
	gDPSetTextureLUT( gp++, G_TT_RGBA16 );

	gSPDisplayList( gp++, PlayerInfos[0].GfxList );


	gDPPipeSync( gp++ );
	gDPSetTexturePersp( gp++, G_TP_NONE );

#if 0
	{
		gSPLoadUcode(gp++,
			     (u64 *)gspF3DEX2_NoN_xbusTextStart,
			     (u64 *)gspF3DEX2_NoN_xbusDataStart);

		gSPSegment(gp++, 0, 0x0);	/* Physical address segment */
		gSPDisplayList(gp++, DLInitRDP);
		gSPDisplayList(gp++, DLInitRSP);
		gSPViewport(gp++,&vpFull);
		gDPSetScissor(gp++,G_SC_NON_INTERLACE,0,0,(currentProcess->ScreenX),(currentProcess->ScreenY));

		gDPPipeSync(gp++);
		gSPPerspNormalize( gp++, perspnorm );
		gDPSetDepthImage(gp++,OS_K0_TO_PHYSICAL(dfb));
		gDPSetColorImage(gp++,G_IM_FMT_RGBA, G_IM_SIZ_16b, currentProcess->ScreenX,OS_K0_TO_PHYSICAL(cfb[db]));
		gDPSetCycleType(gp++,G_CYC_1CYCLE);
		gSPSetGeometryMode(gp++,G_CULL_BACK);
		gDPSetColorDither(gp++,G_CD_MAGICSQ);
		gDPSetTexturePersp( gp++, G_TP_NONE );
	}
#endif

	return( gp );
}


/***************************************************************************
 *
 *
 *
 ***************************************************************************
 *
 *
 *
 ***************************************************************************/
