/*
Copyright (C) 1999-2006 Id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.

This file is part of GtkRadiant.

GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "qdata.h"

#ifdef _WIN32
 #include <windows.h>
#endif

#include <GL/gl.h>

#if 1
	extern char		*g_outputDir;
#endif // _QDATA

char		mip_prefix[1024];		// directory to dump the textures in

qboolean	colormap_issued;
byte		colormap_palette[768];

unsigned total_x = 0;
unsigned total_y = 0;
unsigned total_textures = 0;

#define MAX_IMAGE_SIZE 512

#if	0
/*
==============
RemapZero

Replaces all 0 bytes in an image with the closest palette entry.
This is because NT won't let us change index 0, so any palette
animation leaves those pixels untouched.
==============
*/
void RemapZero (byte *pixels, byte *palette, int width, int height)
{
	int		i, c;
	int		alt_zero;
	int		value, best;

	alt_zero = 0;
	best = 9999999;
	for (i=1 ; i<255 ; i++)
	{
		value = palette[i*3+0]+palette[i*3+1]+palette[i*3+2];
		if (value < best)
		{
			best = value;
			alt_zero = i;
		}
	}

	c = width*height;
	for (i=0 ; i<c ; i++)
		if (pixels[i] == 0)
			pixels[i] = alt_zero;
}

#endif


// ********************************************************************
// **  Mip Map Pre-Processing Routines
// ********************************************************************

#define intensity_value 1

static unsigned image_pal[256];

#define MAX_LAST 25

long palette_r[256], palette_g[256], palette_b[256];
long last_r[MAX_LAST],last_g[MAX_LAST],last_b[MAX_LAST], last_i[MAX_LAST], last_place;

long cached;

void PrepareConvert(unsigned *palette)
{
	int i;

	for(i=0;i<256;i++)
	{
		palette_r[i] = (palette[i] & 0x00ff0000) >> 16;
		palette_g[i] = (palette[i] & 0x0000ff00) >> 8;
		palette_b[i] = (palette[i] & 0x000000ff);
	}

	for(i=0;i<MAX_LAST;i++)
		last_r[i] = -1;

	last_place = -1;
}

int ConvertTrueColorToPal(unsigned r, unsigned g, unsigned b)
{
	int i;
	long min_dist;
	int min_index;
	long dist;
	long dr, dg, db, biggest_delta;

	for(i=0;i<MAX_LAST;i++)
		if (r == last_r[i] && g == last_g[i] && b == last_b[i])
		{
			cached++;
			return last_i[i];
		}

	min_dist = 256 * 256 + 256 * 256 + 256 * 256;
	biggest_delta = 256*256;
	min_index = 0;

	for (i=0;i<256;i++)
	{
		dr = abs(palette_r[i] - r);
		if (dr > biggest_delta)
			continue;
		dg = abs(palette_g[i] - g);
		if (dg > biggest_delta)
			continue;
		db = abs(palette_b[i] - b);
		if (db > biggest_delta)
			continue;

		dist = dr * dr + dg * dg + db * db;
		if (dist < min_dist)
		{
			min_dist = dist;
			min_index = i;
			if (min_dist == 0) break;

			dist = dr;
			if (dg > dist) dist = dg;
			if (db > dist) dist = db;
			if (dist < biggest_delta)
				biggest_delta = dist;
		}
	}

	last_place++;
	if (last_place >= MAX_LAST)
		last_place = 0;

	last_r[last_place] = r;
	last_g[last_place] = g;
	last_b[last_place] = b;
	last_i[last_place] = min_index;

	return min_index;
}


void GL_ResampleTexture8P (byte *in, int inwidth, int inheight, byte *out,  
						   int outwidth, int outheight, palette_t *palette)
{
	int		i, j;
	byte	*inrow, *inrow2;
	unsigned	frac, fracstep;
	unsigned	p1[1024], p2[1024], *p1p, *p2p;
	palette_t	*c1,*c2,*c3,*c4;
	unsigned	r,g,b;
	
	fracstep = inwidth*0x10000/outwidth;

	frac = fracstep>>2;
	for (i=0 ; i<outwidth ; i++)
	{
		p1[i] = frac>>16;
		frac += fracstep;
	}
	frac = 3*(fracstep>>2);
	for (i=0 ; i<outwidth ; i++)
	{
		p2[i] = frac>>16;
		frac += fracstep;
	}

	cached = 0;
	
	for (i=0 ; i<outheight ; i++)//, out += outwidth)
	{
		inrow = in + inwidth*(int)((i+0.25)*inheight/outheight);
		inrow2 = in + inwidth*(int)((i+0.75)*inheight/outheight);

		p1p = p1;
		p2p = p2;
		for (j=0 ; j<outwidth ; j++)
		{
			c1 = &palette[*((byte *)inrow + (*p1p))];
			c2 = &palette[*((byte *)inrow + (*p2p))];
			c3 = &palette[*((byte *)inrow2 + (*p1p++))];
			c4 = &palette[*((byte *)inrow2 + (*p2p++))];

			r = ((unsigned)c1->r + (unsigned)c2->r + (unsigned)c3->r + (unsigned)c4->r)>>2;
			g = ((unsigned)c1->g + (unsigned)c2->g + (unsigned)c3->g + (unsigned)c4->g)>>2;
			b = ((unsigned)c1->b + (unsigned)c2->b + (unsigned)c3->b + (unsigned)c4->b)>>2;

			*out++ = ConvertTrueColorToPal(r,g,b);
		}
	}
}

void GL_MipMap8P(byte *out, byte *in, int width, int height, palette_t *palette)
{
	int			i, j;
	palette_t	*c1,*c2,*c3,*c4;
	unsigned	r,g,b;

	cached = 0;
	memset(out, 0, 256 * 256);
	width <<= 1;
	height <<= 1;

	for (i = 0; i < height; i += 2, in += width)
	{
		for (j = 0; j < width; j += 2)
		{
			c1 = &palette[in[0]];
			c3 = &palette[in[width]];
			in++;
			c2 = &palette[in[0]];
			c4 = &palette[in[width]];
			in++;

			r = ((unsigned)c1->r + (unsigned)c2->r + (unsigned)c3->r + (unsigned)c4->r) >> 2;
			g = ((unsigned)c1->g + (unsigned)c2->g + (unsigned)c3->g + (unsigned)c4->g) >> 2;
			b = ((unsigned)c1->b + (unsigned)c2->b + (unsigned)c3->b + (unsigned)c4->b) >> 2;

			*out++ = ConvertTrueColorToPal(r, g, b);
		}
	}
}


miptex_t *CreateMip(byte *data, unsigned width, unsigned height, byte *palette, int *FinalSize, qboolean mip)
{
	int			scaled_width, scaled_height;
	int			i,j,r,g,b;
	byte		intensitytable[256];
	byte		scaled[256*256];
	byte		out[256*256];
	int			miplevel;
	miptex_t	*mp;
	byte		*pos;
	int			size;

	for (i=0 ; i<256 ; i++)
	{
		j = i * intensity_value;
		if (j > 255)
			j = 255;
		intensitytable[i] = j;
	}

	for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1)
		;
	if (1 && scaled_width > width && 1)
		scaled_width >>= 1;
	for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1)
		;
	if (1 && scaled_height > height && 1)
		scaled_height >>= 1;

	// don't ever bother with >256 textures
	if (scaled_width > 256)
		scaled_width = 256;
	if (scaled_height > 256)
		scaled_height = 256;

	if (scaled_width < 1)
		scaled_width = 1;
	if (scaled_height < 1)
		scaled_height = 1;

	size = sizeof(*mp) + (scaled_width*scaled_height*3);
	mp = (miptex_t *)SafeMalloc(size, "CreateMip");
	memset(mp,0,size);

	mp->version = MIP_VERSION;

	for(i=j=0;i<256;i++,j+=3)
	{
		mp->palette[i].r = r = intensitytable[palette[j]];
		mp->palette[i].g = g = intensitytable[palette[j+1]];
		mp->palette[i].b = b = intensitytable[palette[j+2]];
		image_pal[i] = 0xff000000 | (r<<16) | (g<<8) | (b);
	}

	PrepareConvert(image_pal);

	if (scaled_width == width && scaled_height == height)
	{
		memcpy (scaled, data, width*height);
	}
	else
		GL_ResampleTexture8P (data, width, height, scaled, scaled_width, scaled_height, mp->palette);

	pos = (byte *)(mp + 1);
	miplevel = 0;

	while ((scaled_width >= 1 || scaled_height >= 1) && (miplevel <= MIPLEVELS-1) && (!miplevel || mip))
	{
		if (scaled_width < 1)
			scaled_width = 1;
		if (scaled_height < 1)
			scaled_height = 1;

		if(miplevel > 0)
			GL_MipMap8P(out, (byte *)scaled, scaled_width, scaled_height, mp->palette);
		else
			memcpy(out, scaled, 256 * 256);

		mp->width[miplevel] = scaled_width;
		mp->height[miplevel] = scaled_height;
		mp->offsets[miplevel] = pos - ((byte *)(mp));
		memcpy(pos, out, scaled_width * scaled_height);
		memcpy(scaled, out, 256 * 256);
		pos += scaled_width * scaled_height;

		scaled_width >>= 1;
		scaled_height >>= 1;

		miplevel++;
	}

	*FinalSize = pos - ((byte *)(mp));

	return mp;
}


void GL_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out,  int outwidth, int outheight)
{
	int		i, j;
	unsigned	*inrow, *inrow2;
	unsigned	frac, fracstep;
	unsigned	p1[1024], p2[1024];
	byte		*pix1, *pix2, *pix3, *pix4;

	fracstep = inwidth*0x10000/outwidth;

	frac = fracstep>>2;
	for (i=0 ; i<outwidth ; i++)
	{
		p1[i] = 4*(frac>>16);
		frac += fracstep;
	}
	frac = 3*(fracstep>>2);
	for (i=0 ; i<outwidth ; i++)
	{
		p2[i] = 4*(frac>>16);
		frac += fracstep;
	}

	for (i=0 ; i<outheight ; i++, out += outwidth)
	{
		inrow = in + inwidth*(int)((i+0.25)*inheight/outheight);
		inrow2 = in + inwidth*(int)((i+0.75)*inheight/outheight);
		frac = fracstep >> 1;
		for (j=0 ; j<outwidth ; j++)
		{
			pix1 = (byte *)inrow + p1[j];
			pix2 = (byte *)inrow + p2[j];
			pix3 = (byte *)inrow2 + p1[j];
			pix4 = (byte *)inrow2 + p2[j];
			((byte *)(out+j))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2;
			((byte *)(out+j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2;
			((byte *)(out+j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2;
			((byte *)(out+j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2;
		}
	}
}

void GL_MipMap (byte *out, byte *in, int width, int height)
{
	int		i, j;

	width <<=3;
	height <<= 1;
	for (i=0 ; i<height ; i++, in+=width)
	{
		for (j=0 ; j<width ; j+=8, out+=4, in+=8)
		{
			out[0] = (in[0] + in[4] + in[width+0] + in[width+4])>>2;
			out[1] = (in[1] + in[5] + in[width+1] + in[width+5])>>2;
			out[2] = (in[2] + in[6] + in[width+2] + in[width+6])>>2;
			out[3] = (in[3] + in[7] + in[width+3] + in[width+7])>>2;
		}
	}
}

miptex32_t *CreateMip32(unsigned *data, unsigned width, unsigned height, int *FinalSize, qboolean mip)
{
	int				scaled_width, scaled_height;
	unsigned		scaled[MAX_IMAGE_SIZE*MAX_IMAGE_SIZE];
	unsigned		out[MAX_IMAGE_SIZE*MAX_IMAGE_SIZE];
	int				miplevel;
	miptex32_t		*mp;
	byte			*pos;
	int				size;
	paletteRGBA_t	*test;

	for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1)
		;
	if (1 && scaled_width > width && 1)
		scaled_width >>= 1;
	for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1)
		;
	if (1 && scaled_height > height && 1)
		scaled_height >>= 1;

	// don't ever bother with >256 textures
	if (scaled_width > MAX_IMAGE_SIZE)
		scaled_width = MAX_IMAGE_SIZE;
	if (scaled_height > MAX_IMAGE_SIZE)
		scaled_height = MAX_IMAGE_SIZE;

	if (scaled_width < 1)
		scaled_width = 1;
	if (scaled_height < 1)
		scaled_height = 1;

	size = sizeof(*mp) + (scaled_width*scaled_height*3*4);
	mp = (miptex32_t *)SafeMalloc(size, "CreateMip");
	memset(mp,0,size);

	mp->version = MIP32_VERSION;

	size = width*height;
	test = (paletteRGBA_t *)data;
	while(size)
	{
		if (test->a != 255)
		{
			mp->flags |= LittleLong(SURF_ALPHA_TEXTURE);
			break;
		}

		size--;
		test++;
	}

	if (scaled_width == width && scaled_height == height)
	{
		memcpy (scaled, data, width*height*4);
	}
	else
		GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height);

	pos = (byte *)(mp + 1);
	miplevel = 0;

	while ((scaled_width >= 1 || scaled_height >= 1) && (miplevel <= MIPLEVELS-1) && (!miplevel || mip))
	{
		if (scaled_width < 1)
			scaled_width = 1;
		if (scaled_height < 1)
			scaled_height = 1;

		if (miplevel > 0)
		{
			GL_MipMap((byte *)out, (byte *)scaled, scaled_width, scaled_height);
		}
		else
		{
			memcpy(out, scaled, MAX_IMAGE_SIZE * MAX_IMAGE_SIZE * 4);
		}

		mp->width[miplevel] = scaled_width;
		mp->height[miplevel] = scaled_height;
		mp->offsets[miplevel] = pos - ((byte *)(mp));
		memcpy(pos, out, scaled_width * scaled_height * 4);
		memcpy(scaled, out, MAX_IMAGE_SIZE * MAX_IMAGE_SIZE * 4);
		pos += scaled_width * scaled_height * 4;

		scaled_width >>= 1;
		scaled_height >>= 1;

		miplevel++;
	}

	*FinalSize = pos - ((byte *)(mp));

	return mp;
}

/*
==============
Cmd_Grab

$grab filename x y width height
==============
*/
void Cmd_Grab (void)
{
	int             xl,yl,w,h,y;
	byte			*cropped;
	char			savename[1024];
	char			dest[1024];

	GetScriptToken (false);

	if (token[0] == '/' || token[0] == '\\')
		sprintf (savename, "%s%s.pcx", gamedir, token+1);
	else
		sprintf (savename, "%spics/%s.pcx", gamedir, token);

	if (g_release)
	{
		if (token[0] == '/' || token[0] == '\\')
			sprintf (dest, "%s.pcx", token+1);
		else
			sprintf (dest, "pics/%s.pcx", token);

		ReleaseFile (dest);
		return;
	}

	GetScriptToken (false);
	xl = atoi (token);
	GetScriptToken (false);
	yl = atoi (token);
	GetScriptToken (false);
	w = atoi (token);
	GetScriptToken (false);
	h = atoi (token);

	if (xl<0 || yl<0 || w<0 || h<0 || xl+w>byteimagewidth || yl+h>byteimageheight)
		Error ("GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h);

	// crop it to the proper size
	cropped = (byte *) SafeMalloc (w*h, "Cmd_Grab");
	for (y=0 ; y<h ; y++)
	{
		memcpy (cropped+y*w, byteimage+(y+yl)*byteimagewidth+xl, w);
	}

	// save off the new image
	printf ("saving %s\n", savename);
	CreatePath (savename);
	WritePCXfile (savename, cropped, w,	h, lbmpalette);

	free (cropped);
}

/*
==============
Cmd_Raw

$grab filename x y width height
==============
*/
void Cmd_Raw (void)
{
	int             xl,yl,w,h,y;
	byte			*cropped;
	char			savename[1024];
	char			dest[1024];

	GetScriptToken (false);

	sprintf (savename, "%s%s.lmp", gamedir, token);

	if (g_release)
	{
		sprintf (dest, "%s.lmp", token);
		ReleaseFile (dest);
		return;
	}

	GetScriptToken (false);
	xl = atoi (token);
	GetScriptToken (false);
	yl = atoi (token);
	GetScriptToken (false);
	w = atoi (token);
	GetScriptToken (false);
	h = atoi (token);

	if (xl<0 || yl<0 || w<0 || h<0 || xl+w>byteimagewidth || yl+h>byteimageheight)
		Error ("GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h);

	// crop it to the proper size
	cropped = (byte *) SafeMalloc (w*h, "Cmd_Raw");
	for (y=0 ; y<h ; y++)
	{
		memcpy (cropped+y*w, byteimage+(y+yl)*byteimagewidth+xl, w);
	}

	// save off the new image
	printf ("saving %s\n", savename);
	CreatePath (savename);

	SaveFile (savename, cropped, w*h);

	free (cropped);
}

/*
=============================================================================

COLORMAP GRABBING

=============================================================================
*/

/*
===============
BestColor
===============
*/
byte BestColor (int r, int g, int b, int start, int stop)
{
	int	i;
	int	dr, dg, db;
	int	bestdistortion, distortion;
	int	bestcolor;
	byte	*pal;

//
// let any color go to 0 as a last resort
//
	bestdistortion = 256*256*4;
	bestcolor = 0;

	pal = colormap_palette + start*3;
	for (i=start ; i<= stop ; i++)
	{
		dr = r - (int)pal[0];
		dg = g - (int)pal[1];
		db = b - (int)pal[2];
		pal += 3;
		distortion = dr*dr + dg*dg + db*db;
		if (distortion < bestdistortion)
		{
			if (!distortion)
				return i;		// perfect match

			bestdistortion = distortion;
			bestcolor = i;
		}
	}

	return bestcolor;
}


/*
==============
Cmd_Colormap

$colormap filename

  the brightes colormap is first in the table (FIXME: reverse this now?)

  64 rows of 256 : lightmaps
  256 rows of 256 : translucency table
==============
*/
void Cmd_Colormap (void)
{
	int		levels, brights;
	int		l, c;
	float	frac, red, green, blue;
	float	range;
	byte	*cropped, *lump_p;
	char	savename[1024];
	char	dest[1024];

	colormap_issued = true;
	if (!g_release)
		memcpy (colormap_palette, lbmpalette, 768);

	if (!ScriptTokenAvailable ())
	{	// just setting colormap_issued
		return;
	}

	GetScriptToken (false);
	sprintf (savename, "%spics/%s.pcx", gamedir, token);

	if (g_release)
	{
		sprintf (dest, "pics/%s.pcx", token);
		ReleaseFile (dest);
		return;
	}

	range = 2;
	levels = 64;
	brights = 1;	// ignore 255 (transparent)

	cropped = (byte *) SafeMalloc((levels+256)*256, "Cmd_ColorMap");
	lump_p = cropped;

// shaded levels
	for (l=0;l<levels;l++)
	{
		frac = range - range*(float)l/(levels-1);
		for (c=0 ; c<256-brights ; c++)
		{
			red = lbmpalette[c*3];
			green = lbmpalette[c*3+1];
			blue = lbmpalette[c*3+2];

			red = (int)(red*frac+0.5);
			green = (int)(green*frac+0.5);
			blue = (int)(blue*frac+0.5);
			
//
// note: 254 instead of 255 because 255 is the transparent color, and we
// don't want anything remapping to that
// don't use color 0, because NT can't remap that (or 255)
//
			*lump_p++ = BestColor(red,green,blue, 1, 254);
		}

		// fullbrights allways stay the same
		for ( ; c<256 ; c++)
			*lump_p++ = c;
	}
	
// 66% transparancy table
	for (l=0;l<255;l++)
	{
		for (c=0 ; c<255 ; c++)
		{
			red = lbmpalette[c*3]*0.33 + lbmpalette[l*3]*0.66;
			green = lbmpalette[c*3+1]*0.33 + lbmpalette[l*3+1]*0.66;
			blue = lbmpalette[c*3+2]*0.33 + lbmpalette[l*3+2]*0.66;

			*lump_p++ = BestColor(red,green,blue, 1, 254);
		}
		*lump_p++ = 255;
	}
	for (c=0 ; c<256 ; c++)
		*lump_p++ = 255;
	
	// save off the new image
	printf ("saving %s\n", savename);
	CreatePath (savename);
	WritePCXfile (savename, cropped, 256, levels+256, lbmpalette);

	free (cropped);
}

/*
=============================================================================

MIPTEX GRABBING

=============================================================================
*/

byte	pixdata[256];

int		d_red, d_green, d_blue;

byte	palmap[32][32][32];
qboolean	palmap_built;

/*
=============
FindColor
=============
*/
int FindColor (int r, int g, int b)
{
	int		bestcolor;

	if (r > 255)
		r = 255;
	if (r < 0)
		r = 0;
	if (g > 255)
		g = 255;
	if (g < 0)
		g = 0;
	if (b > 255)
		b = 255;
	if (b < 0)
		b = 0;
#ifndef TABLECOLORS
	bestcolor = BestColor (r, g, b, 0, 254);
#else
	bestcolor = palmap[r>>3][g>>3][b>>3];
#endif

	return bestcolor;
}


void BuildPalmap (void)
{
#ifdef TABLECOLORS
	int		r, g, b;
	int		bestcolor;

	if (palmap_built)
		return;
	palmap_built = true;

	for (r=4 ; r<256 ; r+=8)
	{
		for (g=4 ; g<256 ; g+=8)
		{
			for (b=4 ; b<256 ; b+=8)
			{
				bestcolor = BestColor (r, g, b, 1, 254);
				palmap[r>>3][g>>3][b>>3] = bestcolor;
			}
		}
	}
#endif

	if (!colormap_issued)
		Error ("You must issue a $colormap command first");

}

/*
=============
AveragePixels
=============
*/
byte AveragePixels (int count)
{
	int		r,g,b;
	int		i;
	int		vis;
	int		pix;
	int		bestcolor;
	byte	*pal;
	int		fullbright;
	
	vis = 0;
	r = g = b = 0;
	fullbright = 0;
	for (i=0 ; i<count ; i++)
	{
		pix = pixdata[i];
		
		r += lbmpalette[pix*3];
		g += lbmpalette[pix*3+1];
		b += lbmpalette[pix*3+2];
		vis++;
	}
		
	r /= vis;
	g /= vis;
	b /= vis;

	// error diffusion
	r += d_red;
	g += d_green;
	b += d_blue;
	
//
// find the best color
//
	bestcolor = FindColor (r, g, b);

	// error diffusion
	pal = colormap_palette + bestcolor*3;
	d_red = r - (int)pal[0];
	d_green = g - (int)pal[1];
	d_blue = b - (int)pal[2];

	return bestcolor;
}


typedef enum
{
	pt_contents,
	pt_flags,
	pt_animvalue,
	pt_altnamevalue,
	pt_damagenamevalue,
	pt_flagvalue,
	pt_materialvalue,
	pt_scale,
	pt_mip,
	pt_detail,
	pt_gl,
	pt_nomip,
	pt_detailer,
} parmtype_t;

typedef struct
{
	char	*name;
	int		flags;
	parmtype_t	type;
} mipparm_t;

mipparm_t	mipparms[] =
{
	// utility content attributes
	{"pushpull",CONTENTS_PUSHPULL, pt_contents},
	{"water",	CONTENTS_WATER, pt_contents},
	{"slime",	CONTENTS_SLIME, pt_contents},		// mildly damaging
	{"lava",	CONTENTS_LAVA, pt_contents},		// very damaging
	{"window",	CONTENTS_WINDOW, pt_contents},	// solid, but doesn't eat internal textures
	{"mist",	CONTENTS_MIST, pt_contents},	// non-solid window
	{"origin",	CONTENTS_ORIGIN, pt_contents},	// center of rotating brushes
	{"playerclip",	CONTENTS_PLAYERCLIP, pt_contents},
	{"monsterclip",	CONTENTS_MONSTERCLIP, pt_contents},

	// utility surface attributes
	{"hint",	SURF_HINT, pt_flags},
	{"skip",	SURF_SKIP, pt_flags},
	{"light",	SURF_LIGHT, pt_flagvalue},		// value is the light quantity

	{"animspeed",SURF_ANIMSPEED, pt_flagvalue},		// value will hold the anim speed in fps

	// texture chaining
	{"anim",	0,			pt_animvalue},		// animname is the next animation
	{"alt",		0,			pt_altnamevalue},	// altname is the alternate texture
	{"damage",	0,			pt_damagenamevalue},	// damagename is the damage texture
	{"scale",	0,			pt_scale},		// next two values are for scale
	{"mip",		0,			pt_mip},		
	{"detail",	0,			pt_detail},		

	{"GL_ZERO",					GL_ZERO,				pt_gl},
	{"GL_ONE",					GL_ONE,					pt_gl},
	{"GL_SRC_COLOR",			GL_SRC_COLOR,			pt_gl},
	{"GL_ONE_MINUS_SRC_COLOR",	GL_ONE_MINUS_SRC_COLOR,	pt_gl},
	{"GL_DST_COLOR",			GL_DST_COLOR,			pt_gl},
	{"GL_ONE_MINUS_DST_COLOR",	GL_ONE_MINUS_DST_COLOR,	pt_gl},
	{"GL_SRC_ALPHA",			GL_SRC_ALPHA,			pt_gl},
	{"GL_ONE_MINUS_SRC_ALPHA",	GL_ONE_MINUS_SRC_ALPHA,	pt_gl},
	{"GL_DST_ALPHA",			GL_DST_ALPHA,			pt_gl},
	{"GL_ONE_MINUS_DST_ALPHA",	GL_ONE_MINUS_DST_ALPHA,	pt_gl},
	{"GL_SRC_ALPHA_SATURATE",	GL_SRC_ALPHA_SATURATE,	pt_gl},

	// server attributes
	{"slick",	SURF_SLICK, pt_flags},

	// drawing attributes
	{"sky",		SURF_SKY, pt_flags},
	{"warping",	SURF_WARP, pt_flags},		// only valid with 64x64 textures
	{"trans33",	SURF_TRANS33, pt_flags},	// translucent should allso set fullbright
	{"trans66",	SURF_TRANS66, pt_flags},
	{"flowing",	SURF_FLOWING, pt_flags},	// flow direction towards angle 0
	{"nodraw",	SURF_NODRAW, pt_flags},	// for clip textures and trigger textures
	{"alpha",	SURF_ALPHA_TEXTURE, pt_flags},
	{"undulate",	SURF_UNDULATE, pt_flags},		// rock surface up and down...
	{"skyreflect",	SURF_SKYREFLECT, pt_flags},		// liquid will somewhat reflect the sky - not quite finished....

	{"material", SURF_MATERIAL, pt_materialvalue},
	{"metal",	SURF_TYPE_METAL, pt_flags},
	{"stone",	SURF_TYPE_STONE, pt_flags},
	{"wood",	SURF_TYPE_WOOD, pt_flags},

	{"m_nomip", 0, pt_nomip},
	{"m_detail", 0, pt_detailer},

	{NULL, 0, pt_contents}
};

/*
==============
Cmd_Mip

$mip filename x y width height <OPTIONS>
must be multiples of sixteen
SURF_WINDOW
==============
*/

void Cmd_Mip (void)
{
	int             xl,yl,xh,yh,w,h;
	byte            *dest, *source;
	int				flags, value, contents;
	mipparm_t		*mp;
	char			lumpname[128];
	char			altname[128];
	char			animname[128];
	char			damagename[128];
	byte			buffer[MAX_IMAGE_SIZE*MAX_IMAGE_SIZE];
	unsigned		bufferl[MAX_IMAGE_SIZE*MAX_IMAGE_SIZE];
	materialtype_t	*mat;
	char			filename[1024];
	unsigned        *destl, *sourcel;
	int             linedelta, x, y;
	int				size;
	miptex_t		*qtex;
	miptex32_t		*qtex32;
	float			scale_x, scale_y;
	int				mip_scale;
	// detail texturing
	char			dt_name[128];
	float			dt_scale_x, dt_scale_y;
	float			dt_u, dt_v;
	float			dt_alpha;
	int				dt_src_blend_mode, dt_dst_blend_mode;
	int				flags2;


	GetScriptToken (false);
	strcpy (lumpname, token);
	
	GetScriptToken (false);
	xl = atoi (token);
	GetScriptToken (false);
	yl = atoi (token);
	GetScriptToken (false);
	w = atoi (token);
	GetScriptToken (false);
	h = atoi (token);

	total_x += w;
	total_y += h;
	total_textures++;

	if ( (w & 15) || (h & 15) )
		Error ("line %i: miptex sizes must be multiples of 16", scriptline);

	flags = 0;
	flags2 = 0;
	contents = 0;
	value = 0;
	mip_scale = 0;

	altname[0] = animname[0] = damagename[0] = 0;

	scale_x = scale_y = 0.5;

	// detail texturing
	dt_name[0] = 0;
	dt_scale_x = dt_scale_y = 0.0;
	dt_u = dt_v = 0.0;
	dt_alpha = 0.0;
	dt_src_blend_mode = dt_dst_blend_mode = 0;

	// get optional flags and values
	while (ScriptTokenAvailable ())
	{
		GetScriptToken (false);
	
		for (mp=mipparms ; mp->name ; mp++)
		{
			if (!strcmp(mp->name, token))
			{
				switch (mp->type)
				{
					case pt_animvalue:
						GetScriptToken (false);	// specify the next animation frame
						strcpy (animname, token);
						break;
					case pt_altnamevalue:
						GetScriptToken (false);	// specify the alternate texture
						strcpy (altname, token);
						break;
					case pt_damagenamevalue:
						GetScriptToken (false);	// specify the damage texture
						strcpy (damagename, token);
						break;
					case pt_flags:
						flags |= mp->flags;
						break;
					case pt_contents:
						contents |= mp->flags;
						break;
					case pt_flagvalue:
						flags |= mp->flags;
						GetScriptToken (false);	// specify the light value
						value = atoi(token);
						break;
					case pt_materialvalue:
						GetScriptToken(false);
						for (mat=materialtypes ; mat->name ; mat++)
						{
							if (!strcmp(mat->name, token))
							{
								// assumes SURF_MATERIAL is in top 8 bits
								flags = (flags & 0x0FFFFFF) | (mat->value << 24);
								break;
							}
						}
						break;
					case pt_scale:
						GetScriptToken (false);	// specify the x scale
						scale_x = atof(token);
						GetScriptToken (false);	// specify the y scale
						scale_y = atof(token);
						break;

					case pt_mip:
						mip_scale = 1;
						break;

					case pt_detailer:
						flags2 |= MIP32_DETAILER_FLAG2;
						break;

					case pt_nomip:
						flags2 |= MIP32_NOMIP_FLAG2;
						break;

					case pt_detail:
						GetScriptToken(false);
						strcpy(dt_name, token);
						GetScriptToken(false);
						dt_scale_x = atof(token);
						GetScriptToken(false);
						dt_scale_y = atof(token);
						GetScriptToken(false);
						dt_u = atof(token);
						GetScriptToken(false);
						dt_v = atof(token);
						GetScriptToken(false);
						dt_alpha = atof(token);
						GetScriptToken(false);
						for (mp=mipparms ; mp->name ; mp++)
						{
							if (!strcmp(mp->name, token))
							{
								if (mp->type == pt_gl)
								{
									dt_src_blend_mode = mp->flags;
									break;
								}
							}
						}
						if (!mp->name)
						{
							Error ("line %i: invalid gl blend mode %s", scriptline, token);
						}
						GetScriptToken (false);
						for (mp=mipparms ; mp->name ; mp++)
						{
							if (!strcmp(mp->name, token))
							{
								if (mp->type == pt_gl)
								{
									dt_dst_blend_mode = mp->flags;
									break;
								}
							}
						}
						if (!mp->name)
						{
							Error ("line %i: invalid gl blend mode %s", scriptline, token);
						}
						break;
				}
				break;
			}
		}
		if (!mp->name)
			Error ("line %i: unknown parm %s", scriptline, token);
	}

	if (g_release)
		return;	// textures are only released by $maps

	xh = xl+w;
	yh = yl+h;
	if (xh*yh > MAX_IMAGE_SIZE*MAX_IMAGE_SIZE)
	{
		Error("line %i image %s: image is too big!", scriptline, lumpname);
	}
		
	if (TrueColorImage)
	{
		if (xl >= longimagewidth || xh > longimagewidth ||
			yl >= longimageheight || yh > longimageheight)
		{
			Error ("line %i image %s: bad clip dimmensions (%d,%d) (%d,%d) > image (%d,%d)", scriptline, lumpname, xl,yl,w,h,longimagewidth,longimageheight);
		}

		sourcel = longimage + (yl*longimagewidth) + xl;
		destl = bufferl;
		linedelta = (longimagewidth - w);

		for (y=yl ; y<yh ; y++)
		{
			for (x=xl ; x<xh ; x++)
			{
				*destl++ = *sourcel++;	// RGBA
			}
			sourcel += linedelta;
		}

		qtex32 = CreateMip32(bufferl, w, h, &size, true);

		qtex32->flags |= LittleLong(flags);
		qtex32->flags2 |= LittleLong(flags2);
		qtex32->contents = LittleLong(contents);
		qtex32->value = LittleLong(value);
		qtex32->scale_x = scale_x;
		qtex32->scale_y = scale_y;
		qtex32->mip_scale = mip_scale;
		sprintf (qtex32->name, "%s/%s", mip_prefix, lumpname);
		if (animname[0])
		{
			sprintf (qtex32->animname, "%s/%s", mip_prefix, animname);
		}
		if (altname[0])
		{
			sprintf (qtex32->altname, "%s/%s", mip_prefix, altname);
		}
		if (damagename[0])
		{
			sprintf (qtex32->damagename, "%s/%s", mip_prefix, damagename);
		}
		if (dt_name[0] & ((flags2 & MIP32_DETAILER_FLAG2) == 0)) 
		{
			sprintf (qtex32->dt_name, "%s/%s", mip_prefix, dt_name);
			qtex32->dt_scale_x = dt_scale_x;
			qtex32->dt_scale_y = dt_scale_y;
			qtex32->dt_u = dt_u;
			qtex32->dt_v = dt_v;
			qtex32->dt_alpha = dt_alpha;
			qtex32->dt_src_blend_mode = dt_src_blend_mode;
			qtex32->dt_dst_blend_mode = dt_dst_blend_mode;
		}
		
	//
	// write it out
	//
		sprintf (filename, "%stextures/%s/%s.m32", g_outputDir, mip_prefix, lumpname);
		if(qtex32->flags & (SURF_ALPHA_TEXTURE))
			printf ("writing %s with ALPHA\n", filename);
		else
			printf ("writing %s\n", filename);
		SaveFile (filename, (byte *)qtex32, size);

		free (qtex32);
	}
	else
	{
		if (xl >= byteimagewidth || xh > byteimagewidth ||
			yl >= byteimageheight || yh > byteimageheight)
		{
			Error ("line %i: bad clip dimmensions (%d,%d) (%d,%d) > image (%d,%d)", scriptline, xl,yl,w,h,byteimagewidth,byteimageheight);
		}

		source = byteimage + yl*byteimagewidth + xl;
		dest = buffer;
		linedelta = byteimagewidth - w;

		for (y=yl ; y<yh ; y++)
		{
			for (x=xl ; x<xh ; x++)
			{
				*dest++ = *source++;
			}
			source += linedelta;
		}

		qtex = CreateMip(buffer, w, h, lbmpalette, &size, true);

		qtex->flags = LittleLong(flags);
		qtex->contents = LittleLong(contents);
		qtex->value = LittleLong(value);
		sprintf (qtex->name, "%s/%s", mip_prefix, lumpname);
		if (animname[0])
			sprintf (qtex->animname, "%s/%s", mip_prefix, animname);
		
	//
	// write it out
	//
		sprintf (filename, "%stextures/%s/%s.m8", g_outputDir, mip_prefix, lumpname);
		printf ("writing %s\n", filename);
		SaveFile (filename, (byte *)qtex, size);

		free (qtex);
	}
}

/*
===============
Cmd_Mippal
===============
*/
void Cmd_Mippal (void)
{
	colormap_issued = true;
	if (g_release)
		return;

	memcpy (colormap_palette, lbmpalette, 768);

	BuildPalmap();
}


/*
===============
Cmd_Mipdir
===============
*/
void Cmd_Mipdir (void)
{
	char	filename[1024];

	GetScriptToken (false);
	strcpy (mip_prefix, token);
	// create the directory if needed
	sprintf (filename, "%stextures", g_outputDir);
	Q_mkdir (filename); 
	sprintf (filename, "%stextures/%s", g_outputDir, mip_prefix);
	Q_mkdir (filename); 
}


/*
=============================================================================

ENVIRONMENT MAP GRABBING

Creates six pcx files from tga files without any palette edge seams
also copies the tga files for GL rendering.
=============================================================================
*/

// 3dstudio environment map suffixes
char	*suf[6] = {"rt", "ft", "lf", "bk", "up", "dn"};

/*
=================
Cmd_Environment
=================
*/
void Cmd_Environment (void)
{
	char	name[1024];
	int		i, x, y;
	byte	image[256*256];
	byte	*tga;

	GetScriptToken (false);

	if (g_release)
	{
		for (i=0 ; i<6 ; i++)
		{
			sprintf (name, "env/%s%s.pcx", token, suf[i]);
			ReleaseFile (name);
			sprintf (name, "env/%s%s.tga", token, suf[i]);
			ReleaseFile (name);
		}
		return;
	}
	// get the palette
	BuildPalmap ();

	sprintf (name, "%senv/", gamedir);
	CreatePath (name);

	// convert the images
	for (i=0 ; i<6 ; i++)
	{
		sprintf (name, "%senv/%s%s.tga", gamedir, token, suf[i]);
		printf ("loading %s...\n", name);
		LoadTGA (name, &tga, NULL, NULL);

		for (y=0 ; y<256 ; y++)
		{
			for (x=0 ; x<256 ; x++)
			{
				image[y*256+x] = FindColor (tga[(y*256+x)*4+0],tga[(y*256+x)*4+1],tga[(y*256+x)*4+2]);
			}
		}
		free (tga);
		sprintf (name, "%senv/%s%s.pcx", gamedir, token, suf[i]);
		if (FileTime (name) != -1)
			printf ("%s already exists, not overwriting.\n", name);
		else
			WritePCXfile (name, image, 256, 256, colormap_palette);
	}
}

