

#include "standard.h"
#include "winmain.h"
#include "console.h"
#include "input.h"
#include "hwrender.h"
#include "hwinsert.h"
#include "global.h"
#include "display.h"
#include "init.h"
#include "bgnd.h"
#include "output.h"
#include "drawprimitive.h"
#include "../3dsystem/3d_gen.h"
#include "../3dsystem/3dglodef.h"
#include "process.h"


#include "picture.h"
#include "texture.h"

#include "../game/types.h"
#include "../game/effect2.h"
#include "../game/items.h"

int objbcnt;

bool ThreadDone;
bool ThreadRender;

extern float ZTable[];

void HWR_SubdivPolyList(int num,sint32* sortptr);



/********************************* GLOBAL VARIABLES ******************************************/



extern void S_DrawPercentBar(int x,int y,int bw,int bh,int percent);

defglobal bool GtFullScreenClearNeeded=false;
defglobal bool GtDumpOnly=false;
defglobal RECT GrcDumpWindow;

// in frames (60 per second)
#define SUNSET_TIME (20*60*60)
#define SUNSET_BLUETIME (SUNSET_TIME/2)
#define SUNSET_ORANGETIME (SUNSET_TIME*3/4)

enum lighting_type {LIGHT_NONE, LIGHT_FLICKER, LIGHT_PULSE, LIGHT_SUNSET, NUMBER_LIGHT_TYPES};
int light_level[NUMBER_LIGHT_TYPES];
int wibble_light[WIBBLE_SIZE][32];

int			hires_flag;

int 		wet = 0;
uint8		game_palette[256*3], water_palette[256*3];

/* Animated texture stuff */
sint16		*aranges;

int GnR=0xc0,GnG=0xc0,GnB=0xc0;

#define CLEAN_SCREEN

int save_toggle=0;
#ifdef SCREEN_SHOT

/* Function prototype for screen saving routine */
void save_screen(void);

#ifndef CLEAN_SCREEN
#define CLEAN_SCREEN
#endif
#endif

/*** globals ***/
defglobal bool GtWireFrameMode=false;
defglobal uint8 GabPicturePalette[768];

/*** locals ***/


#include "process.h"



/*********************************** FUNCTION CODE *******************************************/

#ifdef SAVE_CINI
// TEMP
void S_SavePicture(void)
{
	/* Just a stub so that save SaveDDBuffer can be called by the C code */
	SaveDDBuffer(G_FrontBuffer);
}
#endif


/* Initialise anything that needs initialising before each frame can be drawn */

void S_InitialisePolyList(int tClearScreen)
{
	if (GtFullScreenClearNeeded)
	{
		DX_CheckForLostSurfaces();		// ensure surfaces are around before clearing them...
		DD_SpinMessageLoop();			// This might get around the window showing up... As might handling WM_NCPAINT...
		DWORD dwFlags=0;
		dwFlags|=(DXCB_FRONT|DXCB_BACK);
		DX_DoFlipWait();	
		DX_ClearBuffers(dwFlags);
		tClearScreen=false;
		GtFullScreenClearNeeded=false;
	}

	if (App.nRenderMode==RENDERER_INTERNAL)
		DX_ClearBuffers(DXCB_RENDER|DXCB_CLRWINDOW);
	else
	{
		DWORD dwFlags=DXCB_CLRWINDOW;
		if(tClearScreen||(HWConfig.nFillMode < 3))
			dwFlags|=DXCB_BACK;
		dwFlags|=DXCB_ZBUFFER;
		
		DX_ClearBuffers(dwFlags);
	}

	HWR_BeginScene();
	
	phd_InitPolyList();

	objbcnt = 0;


}


extern void "C" S_DumpCine()
{
	static int frames = 0;
	
	if(framedump)
	{
		frames++;
		Log("%d",frames);
		SaveDDBuffer(App.lpFrontBuffer);
		return 1;
	}
	return 0;
}


sint32 S_DumpScreen(void)
	{
	/* Dump the screen to video RAM and return the frame compensating count */
	sint32 		nframes;
	
	if(!framedump)
	{
		nframes = Sync();
		while (nframes < 2)	
		{
			while (!Sync());
			nframes++;
		}
	}
	else nframes = 2;

	
#ifdef SCREEN_SHOT
	/* Save screen shot if SLASH key has been hit */
	if (rawkey(DIK_S))
		{
		save_toggle = !save_toggle;
		while (rawkey(DIK_S))
			S_UpdateInput();
		}
	
	if (save_toggle)
		SaveScreen();
#endif
	ScreenPartialDump();
	
	return(nframes);
	}

void S_ClearScreen(void)
	{
	ScreenClear();
	}

void S_InitialiseScreen(int type)
	{
	/* Set up screen mode, 3d graphics information and whatever else needs setting up when
	the screen is changed */

	if (type>=0)
		{
		if (type != IL_TITLE)
			TempVideoRemove();
		FadeToPal(30, game_palette);
		}
	else
		FadeToPal(0,game_palette);

	if (App.nRenderMode!=RENDERER_INTERNAL)
		HWR_InitState();
 
	
}

/*
extern "C" void SetupDrawThread()
{
	HWR_EnableColorKey(false);
	HWR_EnableZBuffer(true,true);
	
	ThreadDone = false;
	ThreadRender = true;
}
*/

extern int CurrentBucket;


void S_OutputPolyList(void)
	{


#ifdef GAMEDEBUG
	//if (rawkey(DIK_H))
	//	SOUND_DrawOverlay();
	if (rawkey(DIK_Y))
		{
		PHD_VBUF v1,v2,v3,v4;
		PHDTEXTURESTRUCT tex;
		static int nTPage=0;
		static bool tU=false,tI=false;

		float fOOZ=one/f_znear;
		v1.xs=64;v1.ys=64;v1.zv=f_znear;v1.g=0xffff;v1.ooz=fOOZ;v1.clip=0;
		v2.xs=320;v2.ys=64;v2.zv=f_znear;v2.g=0xffff;v2.ooz=fOOZ;v2.clip=0;
		v3.xs=320;v3.ys=320;v3.zv=f_znear;v3.g=0xffff;v3.ooz=fOOZ;v3.clip=0;
		v4.xs=64;v4.ys=320;v4.zv=f_znear;v4.g=0xfffff;v4.ooz=fOOZ;v4.clip=0;
		tex.drawtype=1;
		tex.tpage=nTPage;
		tex.u1=0;tex.v1=0;
		tex.u2=0xffff;tex.v2=0;
		tex.u3=0xffff;tex.v3=0xffff;
		tex.u4=0;tex.v4=0xffff;

		HWI_InsertGT4_Sorted(&v1,&v2,&v3,&v4,&tex,MID_SORT,0);

		if (rawkey(DIK_U))
			{
			if (!tU)
				{
				if (nTPage)
					--nTPage;
				tU=true;
				}
			}
		else
			tU=false;

		if (rawkey(DIK_I))
			{
			if (!tI)
				{
				if (nTPage<255)
					++nTPage;
				tI=true;
				}
			}
		else
			tI=false;
		}
#endif

		if(App.lpZBuffer)
		{
			HWR_EnableColorKey(false);
			HWR_EnableZBuffer(true,true);
		
#ifdef GAMEDEBUG
//			S_DrawPercentBar(0,120,100,5,int((float(CurrentTLVertex-VertexBuffer)/float(MAX_TLVERTICES))*100.0f));

//			for(int n=0;n<MAXBUCKETS;n++)
//			{
//				S_DrawPercentBar(0,120+((n+1)*6),100,5,int((float(Bucket[n].cnt)/float(VERTSPERBUCKET))*100.0f));
//			}
#endif			

			DrawBuckets(0);


//			HWR_SubdivPolyList(surfacenumbf,(sint32*) sort3d_bufferbf);
			phd_SortPolyList(surfacenumbf,sort3d_bufferbf);
			HWR_EnableZBuffer(false,true);
			HWR_DrawPolyListBF(surfacenumbf,(sint32*) sort3d_bufferbf);
//			HWR_DrawPolyList(surfacenumbf,(sint32*) sort3d_bufferbf);
		}
		else
		{
#ifdef GAMEDEBUG
//			S_DrawPercentBar(0,120,100,5,int((float(CurrentTLVertex-VertexBuffer)/float(MAX_TLVERTICES))*100.0f));
		
#endif			
			phd_SortPolyList(surfacenumbf,sort3d_bufferbf);
//			HWR_DrawPolyListBF(surfacenumbf,(sint32*) sort3d_bufferbf);
			HWR_DrawPolyList(surfacenumbf,(sint32*) sort3d_bufferbf);
		}

		HWR_EndScene();
		
#ifdef GAMEDEBUG
		
		
		WinDisplayString(0,0,"fps %.2f",App.fps);
		WinDisplayString(0,1,"DP Calls %d",DrawPrimitiveCnt);
		WinDisplayString(0,2,"BF List %d",surfacenumbf);
		WinDisplayString(0,3,"Polys %d",PolysDrawn);
		WinDisplayString(0,4,"Affine %d",affinepolys);
		WinDisplayString(0,5,"Persp %d",perspolys);

		
		
//		WinDisplayString(0,4,"Room Verts %d",RoomVerts);
//		WinDisplayString(0,5,"Obj Verts %d",ObjVerts);
//		WinDisplayString(0,6,"SBounds Cnt %d",objbcnt);
		RoomVerts = 0;
		ObjVerts  = 0;
#endif



}





/*******************************************************************************
*			Calculate On screen Coordinates Of Bounding Box For Object.
*
* Returns 0 If Object Box totally OFF Screen
*         1 If Object Box Totally ON Screen
*        -1 If Object Box Is Z-Clipped Or X/Y Clipped
******************************************************************************/
int		S_GetObjectBounds( sint16 *bptr )
{
	sint32	minx,maxx;
	sint32	miny,maxy;
	sint32	minz,maxz;
	sint32	zv,coord,numz,i;
	sint32	vertex[8][3];
	sint32	*vertptr,*mptr;

	if ( *(phd_mxptr+M23)>=phd_zfar && outside == 0)
		return(0);
	
	objbcnt++;

	minx = (sint32)*(bptr+0);
	maxx = (sint32)*(bptr+1);
	miny = (sint32)*(bptr+2);
	maxy = (sint32)*(bptr+3);
	minz = (sint32)*(bptr+4);
	maxz = (sint32)*(bptr+5);
	
	vertex[0][0] = minx;		// V.0
	vertex[0][1] = miny;
	vertex[0][2] = minz;
	
	vertex[1][0] = maxx;		// V.1
	vertex[1][1] = miny;
	vertex[1][2] = minz;
	
	vertex[2][0] = maxx;		// V.2
	vertex[2][1] = maxy;
	vertex[2][2] = minz;
	
	vertex[3][0] = minx;		// V.3
	vertex[3][1] = maxy;
	vertex[3][2] = minz;
	
	vertex[4][0] = minx;		// V.4
	vertex[4][1] = miny;
	vertex[4][2] = maxz;
	
	vertex[5][0] = maxx;		// V.5
	vertex[5][1] = miny;
	vertex[5][2] = maxz;
	
	vertex[6][0] = maxx;		// V.6
	vertex[6][1] = maxy;
	vertex[6][2] = maxz;
	
	vertex[7][0] = minx;		// V.7
	vertex[7][1] = maxy;
	vertex[7][2] = maxz;
	
	
	numz = 0;
	minx = miny =  0x3fffffff;
	maxx = maxy = -0x3fffffff;
	mptr = phd_mxptr;
	vertptr = &vertex[0][0];
	for ( i=0; i<8; i++,vertptr+=3 )
	{
		zv = *(mptr+M20) * *(vertptr+0) + *(mptr+M21) * *(vertptr+1) + *(mptr+M22) * *(vertptr+2) +	*(mptr+M23);
		
		if ( zv>phd_znear && zv<phd_zfar )
		{
			numz++;
			zv /= phd_persp;
		
			coord = ( *(mptr+M00) * *(vertptr+0) + 		// Calculate X coordinate....
				 	  *(mptr+M01) * *(vertptr+1) +
					  *(mptr+M02) * *(vertptr+2) +
					  *(mptr+M03) )/zv;
			


			if(coord<minx)
				minx = coord;
		
			if(coord>maxx)
				maxx = coord;
			
			coord = ( *(mptr+M10) * *(vertptr+0) + 		// Calculate Y coordinate....
				  	  *(mptr+M11) * *(vertptr+1) +
					  *(mptr+M12) * *(vertptr+2) +
					  *(mptr+M13) )/zv;

			if(coord<miny)
				miny = coord;
			
			if(coord>maxy)
				maxy = coord;
		}
	}

	minx += phd_centerx;							// Add on Screen Offsets
	maxx += phd_centerx;                            // to MinMax
	miny += phd_centery;
	maxy += phd_centery;
	
	if(numz < 8 && outside > 0) 
		return (-1);
	
	if (numz<8 ||
		minx<0 ||
		miny<0 ||									// Bounding Box is partially
		maxx>phd_winxmax ||                      	// On Whole Window
		maxy>phd_winymax ) 						// or/and Z-Clipped...
		return(-1);
	
	if ( !numz ||									// If No Coords are Visible
		minx>phd_right ||
		miny>phd_bottom ||       					// thro current Doorway then
		maxx<phd_left ||
		maxy<phd_top )								// Exit with ( 0 )
			return(0);

	return(1);										// Return 1 If Box Wholly On Screen...
}


void S_InsertBackPolygon(sint32 min_x, sint32 min_y, sint32 max_x, sint32 max_y, int colour)
{
	/* Rather than clear the whole screen, back polygons are calculated to cover
	the parts of rooms not drawn because they are so far in the distance */
	min_x += phd_winxmin;
	max_x += phd_winxmin;
	min_y += phd_winymin;
	max_y += phd_winymin;
	InsertFlatRect(min_x,min_y,max_x,max_y,phd_zfar+1,inv_colours[C_BLACK]);
}


	/************************************************************************************
	*					Draw Objects Shadow On Screen !!!
	*					Insert Coords Into Object Structure
	*					Then print it with possible clipping..
************************************************************************************/
static	sint16	shadowman[] = {			// Vertices For Shadows
	0,0,0,
		32767,
		1,
		8,
		0,0,0,
		0,0,0,
		0,0,0,
		0,0,0,
		0,0,0,
		0,0,0,
		0,0,0,
		0,0,0 };
	
	
	void	S_PrintShadow( sint16 size, sint16 *bptr, ITEM_INFO *iptr, int unknown )
		{
		int		midx,xadd,xadd2;
		int		midz,zadd,zadd2;
		
		
		midx = (*(bptr+0) + *(bptr+1))/2;
		midz = (*(bptr+4) + *(bptr+5))/2;
		xadd = ((*(bptr+1)-*(bptr+0))*size)/(UNIT_SHADOW*4);
		zadd = ((*(bptr+5)-*(bptr+4))*size)/(UNIT_SHADOW*4);
		xadd2 = xadd*2;
		zadd2 = zadd*2;
		
		shadowman[2+4]  = midx-xadd;	// V.0
		shadowman[4+4]  = midz+zadd2;
		
		shadowman[5+4]  = midx+xadd;   	// V.1
		shadowman[7+4]  = midz+zadd2;
		
		shadowman[8+4]  = midx+xadd2;  	// V.2
		shadowman[10+4] = midz+zadd;
		
		shadowman[11+4] = midx+xadd2;  	// V.3
		shadowman[13+4] = midz-zadd;
		
		shadowman[14+4] = midx+xadd;    // V.4
		shadowman[16+4] = midz-zadd*2;
		
		shadowman[17+4] = midx-xadd;    // V.5
		shadowman[19+4] = midz-zadd*2;
		
		shadowman[20+4] = midx-xadd2;  	// V.6
		shadowman[22+4] = midz-zadd;
		
		shadowman[23+4] = midx-xadd2;  	// V.7
		shadowman[25+4] = midz+zadd;
	
		phd_leftfloat = (float)(phd_winxmin + phd_left);
		phd_topfloat = (float)(phd_winymin + phd_top);
		phd_rightfloat = (float)(phd_winxmin + phd_right + 1);
		phd_bottomfloat = (float)(phd_winymin + phd_bottom + 1);
		f_centerx = (float)(phd_winxmin + phd_centerx);
		f_centery = (float)(phd_winymin + phd_centery);

		phd_PushMatrix();
		phd_TranslateAbs(iptr->pos.x_pos, iptr->floor, iptr->pos.z_pos);
		phd_RotY( iptr->pos.y_rot );
		if ( calc_object_vertices( &shadowman[4] ) )
//			ins_poly_trans8( vbuf, 32 );	// 32 is special transparent depthq table
			InsertTrans8( vbuf, 32 );	// 32 is special transparent depthq table
		phd_PopMatrix();
		}
	
	
		/***********************************************************************************
		*		Calculate Lighting and Depth-Cueing for A 3D Object..
		*			Assumes Current Matrix has already been set up!!
	**********************************************************************************/
#define DEPTHQ_END		(20*1024)
#define DEPTHQ_START	((DEPTHQ_END) - (8*1024))



/****************************************************************************
*			Calculate Light Sourcing for Static Meshes...
***************************************************************************/



	/****************************************************************************
	*			Calculate Light Sourcing for Room Meshes
***************************************************************************/

void S_LightRoom(ROOM_INFO *r)
	{
	sint32 i, j;
	sint32 x, y, z, dlx, dly, dlz, roomx, roomz;
	sint32 distance, falloff_limit, falloff, intensity, radius;
	sint16 *objptr;
	int *wibble;
	LIGHT_INFO *dl;
	
	/* Check on room lighting */
	if (r->lighting != LIGHT_NONE)
/*
	{
		// Unless DYNAMIC_LIT flag is set, there is nothing to do 
		if (r->flags & DYNAMIC_LIT)
			{
			objptr = r->data;
			for (i=(int)*(objptr++); i>0; i--, objptr+=6)
				objptr[5] = objptr[3];
			r->flags &= (~DYNAMIC_LIT);
			}
		}
	else 
*/
		{
		/* Use combo of A/B light levels */
		// wibble = wibble_light[light_level[r->lighting]];

		int nLightLevel=light_level[r->lighting];
		
		objptr = r->data;
		for (i=(int)*(objptr++); i>0; i--, objptr+=6)
			//objptr[5] = (sint16)(objptr[3] + wibble[objptr[4]&0x1f]);	// Dude: masked off new anti-wibble bit
			((uint8*)objptr)[2]+=((nLightLevel*(((uint8*)objptr)[3]&0x1f))>>6);
	}
	
	if (!number_dynamics)
		return;

/*
	
	sint32 i, j;
	sint32 x, y, z, dlx, dly, dlz, roomx, roomz;
	sint32 distance, falloff_limit, falloff, intensity, radius;
	sint16 *objptr,s;
//	int *wibble;
	LIGHT_INFO *dl;

	LnSavedRoomVertices=0;

	// Check on room lighting 
	if (r->lighting != LIGHT_NONE)
	{
		// Use combo of A/B light levels 
//		wibble = wibble_light[light_level[r->lighting]];
		int nLightLevel=light_level[r->lighting];

		objptr = r->data;
		objptr++;
		SaveRoomA(objptr);
		for (i=(int)*(objptr++); i>0; i--, objptr+=2)
			((uint8*)objptr)[2]+=((nLightLevel*(((uint8*)objptr)[3]&0x1f))>>6);
	}

	if (!number_dynamics)
		return;
*/


/*


//	 Work out which dynamic lights will effect this room. Intensity and falloff are
//	specified as powers of 2, so most values are calced using bit shifting 
	roomx = (r->y_size-1) << WALL_SHIFT;
	roomz = (r->x_size-1) << WALL_SHIFT;
	for (i=0, dl=dynamic; i<number_dynamics; i++, dl++)
		{
		dlx = dl->x - r->x;
		dlz = dl->z - r->z;
		
		// Check if room is close enough to light to be affected 
		radius = 1 << dl->falloff;
		if (dlx - radius > roomx)
			continue;
		
		if (dlz - radius > roomz)
			continue;
		
		if (dlx + radius < WALL_L)
			continue;
		
		if (dlz + radius < WALL_L)
			continue;
		
		// Flag for LIGHT_NONE rooms to know to reset shades 
		r->flags |= DYNAMIC_LIT;
		
		// It is, so work out a few more useful values 
		dly = dl->y;
		falloff_limit = 1 << (dl->falloff<<1);
		falloff = (dl->falloff<<1) - dl->intensity;
		intensity = 1 << dl->intensity;
		
		// Go through points in room and modify according to dynamic light 
		objptr = r->data;
		for (j = (int)*(objptr++); j>0; j--, objptr+=6)
			{
			// Don't bother if point is already lit to max intensity 
			if (objptr[5] == 0)
				continue;
			
			// Is point close enough to light to be affected 
			x = objptr[0] - dlx;
			if (x < -radius || x > radius)
				continue;
			
			y = objptr[1] - dly;
			if (y < -radius || y > radius)
				continue;
			
			z = objptr[2] - dlz;
			if (z < -radius || z > radius)
				continue;
			
			distance = SQUARE(x) + SQUARE(y) + SQUARE(z);
			if (distance > falloff_limit)
				continue;
			
			// Now calculate the effect of the light 
			objptr[5] -= (sint16)(intensity - (distance >> falloff));

			
			if (objptr[5] < 0)
				objptr[5] = 0;
			}
		}
	*/

}

/*******************************************************************************
*        Animated texture stuff
******************************************************************************/
#define ANIMATED_TEXTURE 10

void AnimateTextures(int nframes)
	{
	int 				i,j;
	sint16				*ptr;
	PHDTEXTURESTRUCT	temp;
	static int			frame_comp=0;
	
	frame_comp += nframes;
	while ( frame_comp>ANIMATED_TEXTURE )
		{
		ptr = aranges;
		for (i=(int)*(ptr++); i>0; i--,ptr++ )		// Get number of Ranges
			{
			j = (int)*(ptr++);						// Get number of frames-1
			temp = phdtextinfo[*(ptr)];
			for ( ; j>0; j--,ptr++ )
				phdtextinfo[*(ptr)] = phdtextinfo[*(ptr+1)];
			phdtextinfo[*(ptr)] = temp;
			}
		frame_comp -= ANIMATED_TEXTURE;
		}
	}

	/*******************************************************************************
	*        Water palette setup
******************************************************************************/
void S_SetupBelowWater(int underwater)
	{
	/* If camera underwater then blue palette */
	if (underwater ^ wet)
		{
		if (underwater)
			FadeToPal(1, water_palette);
		else
			FadeToPal(1, game_palette);
		
		wet = underwater;
		}
	
	/* Wibble underwater if camera NOT underwater */
	water_effect = !underwater;
	
	/* Always wibble gourauds though */
	shade_effect = 1;

	bBlueEffect=true;
	}


void S_SetupAboveWater(int underwater)
	{
	/* Wibble above water if camera underwater */
	water_effect = underwater;
	shade_effect = 0;
	bBlueEffect=bool(underwater);
	}


	/********************************************************************
	*			Update Game stuff that needs Frame Compensation
	*				such as animating textures
*******************************************************************/
void	S_AnimateTextures( int nframes )
	{
	/* Underwater frame compensated waves */
//	wibble_offset = (wibble_offset+(nframes/2)) & (WIBBLE_SIZE-1);
	wibble_offset = (wibble_offset+(nframes)) & (WIBBLE_SIZE-1);
	
	/* Room lighting effects */
	light_level[LIGHT_FLICKER] = GetRandomDraw() & (WIBBLE_SIZE-1);
	light_level[LIGHT_PULSE] = (phd_sin(wibble_offset * 65536/WIBBLE_SIZE) + (1<<W2V_SHIFT)) * (WIBBLE_SIZE-1) >> (W2V_SHIFT+1);

	if (GF_SunsetEnabled)	// JJG
	{
		sunset += nframes;
		if (sunset < SUNSET_TIME)
			light_level[LIGHT_SUNSET] = sunset * (WIBBLE_SIZE-1) / SUNSET_TIME;
		else
			light_level[LIGHT_SUNSET] = WIBBLE_SIZE-1;
	}
	
	/* Animate Room Textures */
	AnimateTextures(nframes);
	}


/******************************************************************************
 *				Load and decompress PCX piccy file
 *****************************************************************************/
void S_DisplayPicture(char *filename,int tIsTitlePage)
	{
	/* Decompress picture straight into spare buffer + palette into game_palette */

	char name[100];
	HANDLE file;
	DWORD length;
	uint32 dwPackedSize;
	uint8* pPacked;
	uint8* pUnpacked;
	
	lstrcpy(name, filename);
	
	Log(LT_Enter,"S_DisplayPicture: '%s'", name);
#ifdef CD_LOAD
	file = CreateFile(GetFullPath(name), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
#else
	file = CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
#endif
	if (file != INVALID_HANDLE_VALUE)
		{
		if (!tIsTitlePage)
			init_game_malloc();

		dwPackedSize=GetFileSize(file,0);
		pPacked=(uint8*)game_malloc(dwPackedSize,LOAD_PICCY_BUFFER);
		ReadFile(file,pPacked,640*480,&length,NULL);
		CloseHandle(file);
		
		pUnpacked=(uint8*)game_malloc(640*480,LOAD_PICCY_BUFFER);
		DecompPCX(pPacked,dwPackedSize,pUnpacked,GabPicturePalette);

		if (App.nRenderMode==RENDERER_INTERNAL)
			DD_CopyBitmapToSurface(App.lpPictureBuffer,pUnpacked);
		else
			BGND_Make640x480((uint8*)pUnpacked,GabPicturePalette);

		if (!tIsTitlePage)
			{
			SWR_Make236Palette(GabPicturePalette,pUnpacked,640*480,game_palette);
//			memcpy(game_palette,GabPicturePalette,768);
			}

		game_free(dwPackedSize+640*480,LOAD_PICCY_BUFFER);
		}
	else
		Log("DISPLAY PICTURE FAILURE");
  
	}

void S_ConvertPicture()
	{

	
	Log(LT_Enter,"S_ConvertPicture");
	if (!App.lpPictureBuffer)
		{
		Log(LT_Error,"-> G_PictureBuffer==0!");
		return;
		}
	DDSURFACEDESC desc;
	if (!DX_FAILED(DD_LockSurface(App.lpPictureBuffer,desc)))
		{
		SWR_ConvertImage((uint8*)desc.lpSurface,640,480,desc.lPitch,GabPicturePalette,(uint8*)desc.lpSurface,desc.lPitch,game_palette,true);
		DD_UnlockSurface(App.lpPictureBuffer,desc);
		memcpy(GabPicturePalette,game_palette,768);
		}
	else
		Log("DISPLAY PICTURE FAILURE");

  }

void S_DontDisplayPicture(void)
{
	Log(LT_Enter,"S_DontDisplayPicture");
	
	if (App.nRenderMode==RENDERER_HAL)
		BGND_Free();


}

/* Shell functions that need defining for Win95 */
void FadeWait(void)
	{
	}

void ScreenDump(void)
	{
	DX_UpdateFrame(true,0);
	}

void ScreenPartialDump(void)
	{
	DX_UpdateFrame(true,&GrcDumpWindow);
	}

/// this shit was in main.cpp...

int SwitchVideoMode(int width, int height)
	{
	// not even remotely sorted yet!
	return 1;
	}

void FadeToPal(int nframes, uint8 *pal)
{
/*

		int nStart,nCount;

	if (G_RenderInfo.tPaletted)
	{
		int nStart,nCount;
		if (G_RenderInfo.tUse236Palette)
		{
			nStart=10;
			nCount=236;
		}
		else
		{
			nStart=0;
			nCount=256;
		}

		nStart=0;
		nCount=256;

		if (nframes>1)
			{
			PALETTEENTRY current[256], *pEntry=App.PaletteEntries+nStart;
			for (int i=nStart; i<nStart+nCount; i++)
				{
				current[i].peRed = pEntry->peRed;
				current[i].peGreen = pEntry->peGreen;
				current[i].peBlue = pEntry->peBlue;
				pEntry++;
				}

			uint8 *pPal;
			for (int j=0; j<nframes+1; j++)
				{
				pPal=pal+nStart*3;
				pEntry = App.PaletteEntries+nStart;
				for (i=nStart; i<nStart+nCount; i++)
					{
					pEntry->peRed = current[i].peRed + ((*pPal++) - current[i].peRed) * j / nframes;
					pEntry->peGreen = current[i].peGreen + ((*pPal++) - current[i].peGreen) * j / nframes;
					pEntry->peBlue = current[i].peBlue + ((*pPal++) - current[i].peBlue) * j / nframes;
					pEntry++;
					}
				DX_TRY(App.Palette->SetEntries(0,nStart,nCount,App.PaletteEntries+nStart));

				S_DumpScreen();
				}
			}

		else
			{
	  

			PALETTEENTRY* pEntry=App.PaletteEntries+nStart;
			uint8* pPal=pal+nStart*3;
			for (int i=nCount;i;--i)
				{


				pEntry->peRed=*pPal++;
				pEntry->peGreen=*pPal++;
				pEntry->peBlue=*pPal++;
				++pEntry;
				}
			DX_TRY(App.Palette->SetEntries(0,nStart,nCount,App.PaletteEntries+nStart));
			}
		}

*/ 
}

void ScreenClear(bool tWindow)
{
	
	
	//DWORD dwFlags=(G_AppSettings.nRenderMode==RENDERER_INTERNAL)?DXCB_RENDER:DXCB_BACK;
	//if (tWindow)
//		dwFlags|=DXCB_CLRWINDOW;

	DWORD dwFlags = DXCB_BACK;
	DX_ClearBuffers(dwFlags);
	
}


extern "C" void S_CopyScreenToBuffer(void)
{
/*
	
	if (G_AppSettings.nRenderMode==RENDERER_INTERNAL)	
		{
		if (FAILED(G_PictureBuffer->Blt(NULL, G_RenderBuffer, &G_RenderInfo.rcRender, DDBLT_WAIT, NULL)))
			Log("CopyScreenToBuffer flunked");
		DDSURFACEDESC desc;
//		Log("G_PictureBuffer=%x",G_PictureBuffer);
		if (!DX_FAILED(DD_LockSurface(G_PictureBuffer,desc)))
			{
			uint8* pPix=(uint8*)desc.lpSurface;
			uint8* pTab=(uint8*)&(depthq_table[32][0]);
			for (int nY=480;nY--;)
				{
				for (int nX=640;nX--;++pPix)
					*pPix=pTab[*pPix];
				pPix+=(desc.lPitch-640);
				}
			DD_UnlockSurface(G_PictureBuffer,desc);
			}
		memcpy(GabPicturePalette,game_palette,768);
		}
*/
}


void S_CopyBufferToScreen(void)
{

	if(App.nRenderMode==RENDERER_INTERNAL)	
	{
		/*
		if (memcmp(game_palette,GabPicturePalette,768)!=0)
			S_ConvertPicture();

		if (FAILED(G_RenderBuffer->Blt(&G_RenderInfo.rcRender, G_PictureBuffer, NULL, DDBLT_WAIT, NULL)))
			Log("CopyBufferToScreen flunked");
		*/
	}
	else
		BGND_Draw();

}

int DecompPCX(uint8* pSrc,int nLength,uint8* pDest,uint8* pPalette)
	{
	uint8* pIn,* pOut,* pEnd;
	int nW,nH,nPixels;
	char i,c;
	
	PCX_HEADER& header=*(PCX_HEADER*)pSrc;
	if (header.zsoft_flag!=10 ||
		header.version<5 ||
		header.bits!=8 ||
		header.encoding!=1 ||
		header.planes!=1)
		return 0;
	
	nW=header.xmax+1-header.xmin;
	nH=header.ymax+1-header.ymin;
	nPixels=nW*nH;
	
	if (nPixels==0)
		return 0;

	pIn=pSrc+128;
	pOut=pDest;
	pEnd=pOut+nPixels;
	
	while (pOut<pEnd)
		if ((*pIn&0xc0)==0xc0)
			{
			i=*pIn++;
			i&=0x3f;
			c=*pIn++;
			memset(pOut,c,i);
			pOut+=i;
			}
		else
			*pOut++=*pIn++;

	if (pPalette)
		memcpy(pPalette,pSrc+(nLength-768),768);

	return 1;
	}



void mCalcPoint(long dx,long dy,long dz,long *result)
{
	sint32 *mptr;
	mptr = w2v_matrix;

	dx -= *(mptr+M03);
	dy -= *(mptr+M13);
	dz -= *(mptr+M23);
	
	*(result+X)	=	(( *(mptr+M00) * dx		  
					 + *(mptr+M01) * dy
					 + *(mptr+M02) * dz)>> W2V_SHIFT);
	*(result+Y) =	(( *(mptr+M10) * dx
					 + *(mptr+M11) * dy
					 + *(mptr+M12) * dz)>> W2V_SHIFT);
	*(result+Z) =	(( *(mptr+M20) * dx
					 + *(mptr+M21) * dy
					 + *(mptr+M22) * dz)>> W2V_SHIFT);

}




void ProjectPCoord(long x,long y,long z,long *result,long cx,long cy,long fov)
{
	if(z>0)
	{
		*(result+X)=((x*fov)/z)+cx;
		*(result+Y)=((y*fov)/z)+cy;
		*(result+Z)=z;
	}
	else if(z<0)
	{
		*(result+X)=((x*fov)/-z)+cx;
		*(result+Y)=((y*fov)/-z)+cy;
		*(result+Z)=z;
	}
	else
	{
		*(result+X)=((x*fov))+cx;
		*(result+Y)=((y*fov))+cy;
		*(result+Z)=z;
	}
}






