#include "quakedef.h"

//jeank - image libs add
#include "libs/jpeg/jpeglib.h"
#pragma comment ( lib, "libs/jpeg/jpeg.lib")

#include "libs/png/png.h"
#pragma comment(lib, "libs/png/libpng.lib")
//jeank - image libs add

// Tomaz || Image Begin
int		image_width;
int		image_height;
int		image_bits;
int		image_alpha;

/*
===========
JPG Loading
===========
*/

//jeank - jpg vars
byte	*jpeg_rgba;
//jeank - jpg vars

void LoadStructureJPG (FILE *f)
{
    struct jpeg_decompress_struct cinfo;
    JDIMENSION num_scanlines;
    JSAMPARRAY in;
    struct jpeg_error_mgr jerr;
    int numPixels;
    int row_stride;
    byte *out;
    int count;
    int i;

    // set up the decompression.
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress (&cinfo);

    // inititalize the source
    jpeg_stdio_src (&cinfo, f);

    // initialize decompression
    (void) jpeg_read_header (&cinfo, TRUE);
    (void) jpeg_start_decompress (&cinfo);

    // set up the width and height for return
    //matchwidth = cinfo.image_width;
    //matchheight = cinfo.image_height;

	//jkrige - test
	//image_width = cinfo.image_width;
	//image_height = cinfo.image_height;
	//jkrige - test

    // initialize the return data
    numPixels = (cinfo.image_width /*matchwidth*/) * (cinfo.image_height /*matchheight*/);
    jpeg_rgba = malloc ((numPixels * 4));

    // initialize the input buffer - we'll use the in-built memory management routines in the
    // JPEG library because it will automatically free the used memory for us when we destroy
    // the decompression structure. cool.
    row_stride = cinfo.output_width * cinfo.output_components;
    in = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

    // bit of error checking
    if (cinfo.output_components != 3)
        goto error;

    if ((numPixels * 4) != ((row_stride * cinfo.output_height) + numPixels))
        goto error;

    // read the jpeg
    count = 0;

    while (cinfo.output_scanline < cinfo.output_height) 
    {
        num_scanlines = jpeg_read_scanlines(&cinfo, in, 1);
        out = in[0];

        for (i = 0; i < row_stride;)
        {
            jpeg_rgba[count++] = out[i++];
            jpeg_rgba[count++] = out[i++];
            jpeg_rgba[count++] = out[i++];
            jpeg_rgba[count++] = 255;
        }
    }

    // finish decompression and destroy the jpeg
    (void) jpeg_finish_decompress (&cinfo);
    jpeg_destroy_decompress (&cinfo);
	fclose(f);

	image_width = cinfo.image_width;
	image_height = cinfo.image_height;
	image_bits = 32;
	image_alpha = 3;

	if(jpeg_rgba)
		free(jpeg_rgba);

    return;

error:
    Con_DPrintf ("Invalid JPEG Format\n");
    jpeg_destroy_decompress (&cinfo);
	fclose(f);

	if(jpeg_rgba)
		free(jpeg_rgba);

    return;
}

byte* LoadJPG (FILE *f /*, int matchwidth, int matchheight*/)
{
    struct jpeg_decompress_struct cinfo;
    JDIMENSION num_scanlines;
    JSAMPARRAY in;
    struct jpeg_error_mgr jerr;
    int numPixels;
    int row_stride;
    byte *out;
    int count;
    int i;

    // set up the decompression.
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress (&cinfo);

    // inititalize the source
    jpeg_stdio_src (&cinfo, f);

    // initialize decompression
    (void) jpeg_read_header (&cinfo, TRUE);
    (void) jpeg_start_decompress (&cinfo);

    // set up the width and height for return
    //matchwidth = cinfo.image_width;
    //matchheight = cinfo.image_height;

	//jkrige - test
	//image_width = cinfo.image_width;
	//image_height = cinfo.image_height;
	//jkrige - test

    // initialize the return data
    numPixels = (cinfo.image_width /*matchwidth*/) * (cinfo.image_height /*matchheight*/);
    jpeg_rgba = malloc ((numPixels * 4));

    // initialize the input buffer - we'll use the in-built memory management routines in the
    // JPEG library because it will automatically free the used memory for us when we destroy
    // the decompression structure. cool.
    row_stride = cinfo.output_width * cinfo.output_components;
    in = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

    // bit of error checking
    if (cinfo.output_components != 3)
        goto error;

    if ((numPixels * 4) != ((row_stride * cinfo.output_height) + numPixels))
        goto error;

    // read the jpeg
    count = 0;

    while (cinfo.output_scanline < cinfo.output_height) 
    {
        num_scanlines = jpeg_read_scanlines(&cinfo, in, 1);
        out = in[0];

        for (i = 0; i < row_stride;)
        {
            jpeg_rgba[count++] = out[i++];
            jpeg_rgba[count++] = out[i++];
            jpeg_rgba[count++] = out[i++];
            jpeg_rgba[count++] = 255;
        }
    }

    // finish decompression and destroy the jpeg
    (void) jpeg_finish_decompress (&cinfo);
    jpeg_destroy_decompress (&cinfo);
	fclose(f);

	image_width = cinfo.image_width;
	image_height = cinfo.image_height;
	image_bits = 32;
	image_alpha = 3;

    return jpeg_rgba;

error:
	Con_Printf ("Invalid JPEG Format\n");
    //Con_DPrintf ("Invalid JPEG Format\n");
    jpeg_destroy_decompress (&cinfo);
	fclose(f);

	if(jpeg_rgba)
		free(jpeg_rgba);

    return NULL;
}

/*
===========
PCX Loading
===========
*/

typedef struct
{
	char		manufacturer;
	char		version;
	char		encoding;
	char		bits_per_pixel;
	unsigned short	xmin,ymin,xmax,ymax;
	unsigned short	hres,vres;
	unsigned char	palette[48];
	char		reserved;
	char		color_planes;
	unsigned short	bytes_per_line;
	unsigned short	palette_type;
	char		filler[58];
	unsigned 	data;			// unbounded
} pcx_t;

/*
========
LoadPCX
========
*/

//byte* LoadPCX (FILE *f, int matchwidth, int matchheight)
byte* LoadPCX (FILE *f,char* name)
{
	pcx_t	*pcx, pcxbuf;
	byte	palette[768];
	byte	*pix, *image_rgba;
	int		x, y;
	int		dataByte, runLength;
	int		count;

//
// parse the PCX file
//
	fread (&pcxbuf, 1, sizeof(pcxbuf), f);

	pcx = &pcxbuf;

	if (pcx->manufacturer != 0x0a
		|| pcx->version != 5
		|| pcx->encoding != 1
		|| pcx->bits_per_pixel != 8
		|| pcx->xmax > 320
		|| pcx->ymax > 256)
	{
		Con_Printf ("LoadPCX: %s Bad PCX file format\n",name);
		return NULL;
	}

    if (pcx->xmax%2 == 1 || pcx->ymax%2 == 1)
        {
        Con_Printf("LoadPCX: Texture dimensions not even %s (%dx%d)\n",name,pcx->xmax, pcx->ymax);
        return NULL;
        }

    if (pcx->xmax%16 != 0 || pcx->ymax%16 != 0)
        {
        Con_Printf("LoadPCX: Texture dimensions not 16 aligned %s (%dx%d)\n",name,pcx->xmax, pcx->ymax);
        return NULL;
        }

	/*
    if (matchwidth && (pcx->xmax+1) != matchwidth)
		return NULL;
	if (matchheight && (pcx->ymax+1) != matchheight)
		return NULL;
    */

	// seek to palette
	fseek (f, -768, SEEK_END);
	fread (palette, 1, 768, f);

	fseek (f, sizeof(pcxbuf) - 4, SEEK_SET);

	count = (pcx->xmax+1) * (pcx->ymax+1);
	image_rgba = malloc( count * 4);

	for (y=0 ; y<=pcx->ymax ; y++)
	{
		pix = image_rgba + 4*y*(pcx->xmax+1);
		for (x=0 ; x<=pcx->xmax ; )
		{
			dataByte = fgetc(f);

			if((dataByte & 0xC0) == 0xC0)
			{
				runLength = dataByte & 0x3F;
				dataByte = fgetc(f);
			}
			else
				runLength = 1;

			while(runLength-- > 0)
			{
				pix[0] = palette[dataByte*3];
				pix[1] = palette[dataByte*3+1];
				pix[2] = palette[dataByte*3+2];
				pix[3] = 255;
				pix += 4;
				x++;
			}
		}
	}
	fclose(f);
	image_width = pcx->xmax+1;
	image_height = pcx->ymax+1;
	return image_rgba;
}

/*
=============
TARGA LOADING
=============
*/

typedef struct _TargaHeader 
{
	unsigned char 	id_length, colormap_type, image_type;
	unsigned short	colormap_index, colormap_length;
	unsigned char	colormap_size;
	unsigned short	x_origin, y_origin, width, height;
	unsigned char	pixel_size, attributes;
} TargaHeader;


TargaHeader		targa_header;

int fgetLittleShort (FILE *f)
{
	byte	b1, b2;

	b1 = fgetc(f);
	b2 = fgetc(f);

	return (short)(b1 + b2*256);
}

int fgetLittleLong (FILE *f)
{
	byte	b1, b2, b3, b4;

	b1 = fgetc(f);
	b2 = fgetc(f);
	b3 = fgetc(f);
	b4 = fgetc(f);

	return b1 + (b2<<8) + (b3<<16) + (b4<<24);
}


/*
========
LoadTGA
========
*/
#define USE_EXTTEX 

byte* LoadTGA (FILE *fin, char *name)
{
	int		columns, rows, numPixels;
	byte	*pixbuf;
	int		row, column;
	byte	*image_rgba;

	targa_header.id_length			= fgetc(fin);
	targa_header.colormap_type		= fgetc(fin);
	targa_header.image_type			= fgetc(fin);
	
	targa_header.colormap_index		= fgetLittleShort(fin);
	targa_header.colormap_length	= fgetLittleShort(fin);
	targa_header.colormap_size		= fgetc(fin);
	targa_header.x_origin			= fgetLittleShort(fin);
	targa_header.y_origin			= fgetLittleShort(fin);
	targa_header.width				= fgetLittleShort(fin);
	targa_header.height				= fgetLittleShort(fin);

	targa_header.pixel_size			= fgetc(fin);
	targa_header.attributes			= fgetc(fin);

	if (targa_header.image_type!=2 && targa_header.image_type!=10) 
		Host_Error ("LoadTGA: %s Only type 2 and 10 targa RGB images supported\n", name);

	if (targa_header.colormap_type !=0 || (targa_header.pixel_size!=32 && targa_header.pixel_size!=24))
		Host_Error ("LoadTGA: %s Only 32 or 24 bit images supported (no colormaps)\n", name);


    //Con_Printf("TGA ATTRIB : %d \n",targa_header.attributes);
    if (targa_header.width%2 == 1 || targa_header.height%2 == 1)
        {
        Con_Printf("LoadTGA: Texture dimensions not even %s (%dx%d)\n",name,targa_header.width, targa_header.height);
        return NULL;
        }

    if (targa_header.width%16 != 0 || targa_header.height%16 != 0)
        {
        Con_Printf("LoadTGA: Texture dimensions not 16 aligned %s (%dx%d)\n",name,targa_header.width, targa_header.height);
        return NULL;
        }

	columns = targa_header.width;
	rows = targa_header.height;
	numPixels = columns * rows;

	image_rgba = malloc (numPixels*4);
	
	if (targa_header.id_length != 0)
		fseek(fin, targa_header.id_length, SEEK_CUR);  // skip TARGA image comment
	
	if (targa_header.image_type==2) 
	{  // Uncompressed, RGB images
        if ((targa_header.attributes & 32) == 0)
            {
            for(row=rows-1; row>=0; row--) 
		        {
			    pixbuf = image_rgba + row*columns*4;
			    for(column=0; column<columns; column++) 
			        {
				    unsigned char red = 0,green = 0,blue = 0,alphabyte = 0;
				    switch (targa_header.pixel_size) 
				        {
					    case 24:
							    
							    blue		= getc(fin);
							    green		= getc(fin);
							    red			= getc(fin);
							    pixbuf[0]	= red;
							    pixbuf[1]	= green;
							    pixbuf[2]	= blue;
							    pixbuf[3]	= 255;
							    pixbuf		+= 4;
							    break;
					    case 32:
							    blue		= getc(fin);
							    green		= getc(fin);
							    red			= getc(fin);
							    alphabyte	= getc(fin);
							    pixbuf[0]	= red;
							    pixbuf[1]	= green;
							    pixbuf[2]	= blue;
							    pixbuf[3]	= alphabyte;
							    pixbuf		+= 4;
							    break;
				        }
			        }
		        }
            }
        else 
            {
            for(row = 0; row <= rows-1; row++) 
		        {
			    pixbuf = image_rgba + row*columns*4;
			    for(column=0; column<columns; column++) 
			        {
				    unsigned char red = 0,green = 0,blue = 0,alphabyte = 0;
				    switch (targa_header.pixel_size) 
				        {
					    case 24:
							    
							    blue		= getc(fin);
							    green		= getc(fin);
							    red			= getc(fin);
							    pixbuf[0]	= red;
							    pixbuf[1]	= green;
							    pixbuf[2]	= blue;
							    pixbuf[3]	= 255;
							    pixbuf		+= 4;
							    break;
					    case 32:
							    blue		= getc(fin);
							    green		= getc(fin);
							    red			= getc(fin);
							    alphabyte	= getc(fin);
							    pixbuf[0]	= red;
							    pixbuf[1]	= green;
							    pixbuf[2]	= blue;
							    pixbuf[3]	= alphabyte;
							    pixbuf		+= 4;
							    break;
				        }
			        }
		        }
            }
	}
	else if (targa_header.image_type == 10) 
	{   // Runlength encoded RGB images
		unsigned char red = 0,green = 0,blue = 0,alphabyte = 0,packetHeader,packetSize,j;
		for(row=rows-1; row>=0; row--) 
		{
			pixbuf = image_rgba + row*columns*4;
			for(column=0; column<columns; ) 
			{
				packetHeader=getc(fin);
				packetSize = 1 + (packetHeader & 0x7f);
				if (packetHeader & 0x80) 
				{   // run-length packet
					switch (targa_header.pixel_size) 
					{
						case 24:
								blue		= getc(fin);
								green		= getc(fin);
								red			= getc(fin);
								alphabyte	= 255;
								break;
						case 32:
								blue		= getc(fin);
								green		= getc(fin);
								red			= getc(fin);
								alphabyte	= getc(fin);
								break;
					}
					for(j=0;j<packetSize;j++) 
					{
						pixbuf[0]	= red;
						pixbuf[1]	= green;
						pixbuf[2]	= blue;
						pixbuf[3]	= alphabyte;
						pixbuf		+= 4;
						column++;
						if (column==columns) 
						{ // run spans across rows
							column=0;
							if (row>0)
								row--;
							else
								goto breakOut;
							pixbuf = image_rgba + row*columns*4;
						}
					}
				}
				else 
				{	// non run-length packet
					for(j=0;j<packetSize;j++) 
					{
						switch (targa_header.pixel_size)
						{
							case 24:
									blue		= getc(fin);
									green		= getc(fin);
									red			= getc(fin);
									pixbuf[0]	= red;
									pixbuf[1]	= green;
									pixbuf[2]	= blue;
									pixbuf[3]	= 255;
									pixbuf		+= 4;
									break;
							case 32:
									blue		= getc(fin);
									green		= getc(fin);
									red			= getc(fin);
									alphabyte	= getc(fin);
									pixbuf[0]	= red;
									pixbuf[1]	= green;
									pixbuf[2]	= blue;
									pixbuf[3]	= alphabyte;
									pixbuf		+= 4;
									break;
						}
						column++;
						if (column==columns) 
						{ // pixel packet run spans across rows
							column=0;
							if (row>0)
								row--;
							else
								goto breakOut;
							pixbuf = image_rgba + row*columns*4;
						}
					}
				}
			}
			breakOut:;
		}

	 if ((targa_header.attributes & 32) != 0) 
        {
        long tmp_size = columns*4;
        unsigned char* tmp_data = (unsigned char *) malloc(tmp_size);
        
        if (tmp_data != NULL)
            {
            for(row=rows-1; row >= rows/2; row--) 
		        {
		        memcpy(tmp_data,image_rgba+(row*tmp_size),tmp_size);
                memcpy(image_rgba+(row*tmp_size),image_rgba+(((rows-1)-row)*tmp_size),tmp_size);                   
                memcpy(image_rgba+(((rows-1)-row)*tmp_size),tmp_data,tmp_size);                   
                }
           free(tmp_data);
           }
       }
    }
	
	fclose(fin);
	image_width = columns;
	image_height = rows;
	return image_rgba;
}


/*
========
LoadPNG
========
*/
static struct
{
	const unsigned char	*tmpBuf;
	int		tmpBuflength;
	int		tmpi;
	//int		FBgColor;
	//int		FTransparent;
	unsigned int	FRowBytes;
	//double	FGamma;
	//double	FScreenGamma;
	unsigned char	**FRowPtrs;
	unsigned char	*Data;
	//char	*Title;
	//char	*Author;
	//char	*Description;
	int		BitDepth;
	int		BytesPerPixel;
	int		ColorType;
	unsigned long	Height; // retarded libpng 1.2 pngconf.h uses long (64bit/32bit depending on arch)
	unsigned long	Width; // retarded libpng 1.2 pngconf.h uses long (64bit/32bit depending on arch)
	int		Interlace;
	int		Compression;
	int		Filter;
	//double	LastModified;
	//int		Transparent;
	//qfile_t *outfile;
} my_png;

void PNG_fReadData(void *png, unsigned char *data, size_t length)
{
	size_t l;
	l = my_png.tmpBuflength - my_png.tmpi;
	if (l < length)
	{
		Con_Printf("PNG_fReadData: overrun by %i bytes\n", (int)(length - l));
		// a read going past the end of the file, fill in the remaining bytes
		// with 0 just to be consistent
		memset(data + l, 0, length - l);
		length = l;
	}
	memcpy(data, my_png.tmpBuf + my_png.tmpi, length);
	my_png.tmpi += (int)length;
	//Com_HexDumpToConsole(data, (int)length);
}

void PNG_error_fn(void *png, const char *message)
{
	Con_Printf("PNG_LoadImage: error: %s\n", message);
}

void PNG_warning_fn(void *png, const char *message)
{
	Con_Printf("PNG_LoadImage: warning: %s\n", message);
}

unsigned char *LoadPNG (FILE *fin)
{
	unsigned int c;
	unsigned int	y;
	//void *png, *pnginfo;
	unsigned char *imagedata = NULL;
	unsigned char ioBuffer[8192];
	png_structp png;
	png_infop pnginfo;

	unsigned char *raw;
	int filesize;

	fseek(fin, 0, SEEK_END);
	filesize = ftell(fin);
	fseek(fin, 0, SEEK_SET);
	
	raw = (byte*)malloc(filesize+1);
	
	fread (raw, 1, filesize, fin);

	if(png_sig_cmp(raw, 0, filesize))
	{
		free(raw);
		fclose(fin);
		return NULL;
	}
	png = (void *)png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, PNG_error_fn, PNG_warning_fn);
	if(!png)
	{
		free(raw);
		fclose(fin);
		return NULL;
	}

	// this must be memset before the setjmp error handler, because it relies
	// on the fields in this struct for cleanup
	memset(&my_png, 0, sizeof(my_png));

	if (setjmp(png->jmpbuf))
	{
		if (my_png.Data)
			free(my_png.Data);
		my_png.Data = NULL;
		if (my_png.FRowPtrs)
			free(my_png.FRowPtrs);
		my_png.FRowPtrs = NULL;
		png_destroy_read_struct(&png, &pnginfo, NULL);
		free(raw);
		fclose(fin);
		return NULL;
	}
	//

	pnginfo = png_create_info_struct(png);
	if(!pnginfo)
	{
		png_destroy_read_struct(&png, &pnginfo, NULL);
		free(raw);
		fclose(fin);
		return NULL;
	}
	png_set_sig_bytes(png, 0);

	my_png.tmpBuf = raw;
	my_png.tmpBuflength = filesize;
	my_png.tmpi = 0;
	//my_png.Data		= NULL;
	//my_png.FRowPtrs	= NULL;
	//my_png.Height		= 0;
	//my_png.Width		= 0;
	my_png.ColorType	= PNG_COLOR_TYPE_RGB;
	//my_png.Interlace	= 0;
	//my_png.Compression	= 0;
	//my_png.Filter		= 0;
	png_set_read_fn(png, ioBuffer, PNG_fReadData);
	png_read_info(png, pnginfo);
	png_get_IHDR(png, pnginfo, &my_png.Width, &my_png.Height,&my_png.BitDepth, &my_png.ColorType, &my_png.Interlace, &my_png.Compression, &my_png.Filter);

	// this check guards against pngconf.h with unsigned int *width/height parameters on big endian systems by detecting the strange values and shifting them down 32bits
	// (if it's little endian the unwritten bytes are the most significant
	//  ones and we don't worry about that)
	//
	// this is only necessary because of retarded 64bit png_uint_32 types in libpng 1.2, which can (conceivably) vary by platform
#if LONG_MAX > 4000000000
	if (my_png.Width > LONG_MAX || my_png.Height > LONG_MAX)
	{
		my_png.Width >>= 32;
		my_png.Height >>= 32;
	}
#endif

	if (my_png.ColorType == PNG_COLOR_TYPE_PALETTE)
		png_set_palette_to_rgb(png);
	if (my_png.ColorType == PNG_COLOR_TYPE_GRAY || my_png.ColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
	{
		png_set_gray_to_rgb(png);
		if (my_png.BitDepth < 8)
			png_set_gray_1_2_4_to_8(png);
	}

	if (png_get_valid(png, pnginfo, PNG_INFO_tRNS))
		png_set_tRNS_to_alpha(png);
	if (my_png.BitDepth == 8 && !(my_png.ColorType  & PNG_COLOR_MASK_ALPHA))
		png_set_filler(png, 255, 1);
	if (( my_png.ColorType == PNG_COLOR_TYPE_GRAY) || (my_png.ColorType == PNG_COLOR_TYPE_GRAY_ALPHA ))
		png_set_gray_to_rgb(png);
	if (my_png.BitDepth < 8)
		png_set_expand(png);

	png_read_update_info(png, pnginfo);

	my_png.FRowBytes = png_get_rowbytes(png, pnginfo);
	my_png.BytesPerPixel = png_get_channels(png, pnginfo);

	my_png.FRowPtrs = (unsigned char **)malloc(my_png.Height * sizeof(*my_png.FRowPtrs));
	if (my_png.FRowPtrs)
	{
		imagedata = (unsigned char *)malloc(my_png.Height * my_png.FRowBytes);
		if(imagedata)
		{
			my_png.Data = imagedata;
			for(y = 0;y < my_png.Height;y++)
				my_png.FRowPtrs[y] = my_png.Data + y * my_png.FRowBytes;
			png_read_image(png, my_png.FRowPtrs);
		}
		else
		{
			Con_Printf("PNG_LoadImage : not enough memory\n");
			png_destroy_read_struct(&png, &pnginfo, NULL);
			free(my_png.FRowPtrs);
			free(raw);
			fclose(fin);
			return NULL;
		}
		free(my_png.FRowPtrs);
		my_png.FRowPtrs = NULL;
	}
	else
	{
		Con_Printf("PNG_LoadImage : not enough memory\n");
		png_destroy_read_struct(&png, &pnginfo, NULL);
		free(raw);
		fclose(fin);
		return NULL;
	}

	png_read_end(png, pnginfo);
	png_destroy_read_struct(&png, &pnginfo, NULL);

	image_width = (int)my_png.Width;
	image_height = (int)my_png.Height;

	if (my_png.BitDepth != 8)
	{
		Con_Printf ("PNG_LoadImage : bad color depth\n");
		free(imagedata);
		free(raw);
		fclose(fin);
		return NULL;
	}

	// swizzle RGBA to BGRA
	for (y = 0;y < (unsigned int)(image_width*image_height*4);y += 4)
	{
		c = imagedata[y+0];
		imagedata[y+0] = imagedata[y+2];
		imagedata[y+2] = c;
	}
	free(raw);
	fclose(fin);
	return imagedata;
}


/*****************************************************************************/
/*                                                                           */
/* dds images                                                                */
/*                                                                           */
/*****************************************************************************/

typedef struct {
	unsigned int dwColorSpaceLowValue;
	unsigned int dwColorSpaceHighValue;
} dds_colorkey;

typedef struct {
	unsigned int magic;
	unsigned int dwSize;
	unsigned int dwFlags;
	unsigned int dwHeight;
	unsigned int dwWidth;
	long lPitch;
	unsigned int dwDepth;
	unsigned int dwMipMapCount;
	unsigned int dwAlphaBitDepth;
	unsigned int dwReserved;
	void *lpSurface;
	dds_colorkey ddckCKDestOverlay;
	dds_colorkey ddckCKDestBlt;
	dds_colorkey ddckCKSrcOverlay;
	dds_colorkey ddckCKSrcBlt;
	unsigned int dwPFSize;
	unsigned int dwPFFlags;
	unsigned int dwFourCC;
	unsigned int dwRGBBitCount;
	unsigned int dwRBitMask;
	unsigned int dwGBitMask;
	unsigned int dwBBitMask;
	unsigned int dwRGBAlphaBitMask;
	unsigned int dwCaps;
	unsigned int dwCaps2;
	unsigned int dwCaps3;
	unsigned int dwVolumeDepth;
	unsigned int dwTextureStage;
} dds_header;

typedef struct {
	unsigned char r;
	unsigned char g;
	unsigned char b;
} dds_color;

enum {
	DDS_ERROR = -1,
	DDS_RGB,
	DDS_RGBA,
	DDS_DXT1,
	DDS_DXT2,
	DDS_DXT3,
	DDS_DXT4,
	DDS_DXT5
};

enum {
	DDPF_ALPHAPIXELS = 0x01,
	DDPF_FOURCC = 0x04,
	DDPF_RGB = 0x40,
	DDPF_RGBA = 0x41
};


unsigned char *LoadDDS(FILE *file)
{
	int format, x, y, i, j;
	unsigned char *data, *buf, *src, *dest;
	unsigned int c0, c1;
	dds_header header;
	dds_color color[4];

	if(!file)
		return NULL;

	fread(&header, sizeof(dds_header), 1, file);
	if(header.magic != ('D' | 'D' << 8 | 'S' << 16 | ' ' << 24))
	{
		fclose(file);
		return NULL;
	}
	image_width = header.dwWidth;
	image_height = header.dwHeight;
	format = DDS_ERROR;
	if	(header.dwPFFlags & DDPF_FOURCC)
	{
		if		(header.dwFourCC == ('D' | ('X' << 8) | ('T' << 16) | ('1' << 24))) format = DDS_DXT1;
		else if	(header.dwFourCC == ('D' | ('X' << 8) | ('T' << 16) | ('2' << 24))) format = DDS_DXT2;
		else if	(header.dwFourCC == ('D' | ('X' << 8) | ('T' << 16) | ('3' << 24))) format = DDS_DXT3;
		else if	(header.dwFourCC == ('D' | ('X' << 8) | ('T' << 16) | ('4' << 24))) format = DDS_DXT4;
		else if	(header.dwFourCC == ('D' | ('X' << 8) | ('T' << 16) | ('5' << 24))) format = DDS_DXT5;
	}
	else if	(header.dwPFFlags == DDPF_RGB && header.dwRGBBitCount == 24) format = DDS_RGB;
	else if	(header.dwPFFlags == DDPF_RGBA && header.dwRGBBitCount == 32) format = DDS_RGBA;
	if	(format == DDS_ERROR) 
	{
		fclose(file);
		return NULL;
	}
	if(format == DDS_DXT2 || format == DDS_DXT4)
	{
		fclose(file);
		return NULL;
	}
	data = (unsigned char *)malloc(image_width * image_height * 4);
	if(format == DDS_RGB) 
	{
		buf = (unsigned char *)malloc(image_width * image_height * 3);
		fread(buf,image_width * image_height * 3,1,file);
		src = buf;
		dest = data;

		for(y = 0; y < image_height; y++) 
		{
			for(x = 0; x < image_width; x++) 
			{
				*dest++ = *src++;
				*dest++ = *src++;
				*dest++ = *src++;
				*dest++ = 255;
			}
		}

		free(buf);
	}
	else if(format == DDS_RGBA)
	{
		buf = (unsigned char *)malloc(image_width * image_height * 4);
		fread(buf,image_width * image_height * 4,1,file);
		src = buf;
		dest = data;
		
		for(y = 0; y < image_height; y++)
		{
			for(x = 0; x < image_width; x++)
			{
				*dest++ = *src++;
				*dest++ = *src++;
				*dest++ = *src++;
				*dest++ = *src++;
			}
		}
		
		free(buf);
	}
	else 
	{
		buf = (unsigned char *)malloc(image_width * image_height);
		src = buf;
		fread(buf,image_width * image_height,1,file);
		
		for(y = 0; y < image_height; y += 4)
		{
			for(x = 0; x < image_width; x += 4)
			{
				unsigned long alpha = 0;
				unsigned int a0 = 0;
				unsigned int a1 = 0;
				if(format == DDS_DXT3) 
				{
					alpha = *(unsigned long*)src;
					src += 8;
				} 
				else if(format == DDS_DXT5) 
				{
					alpha = (*(unsigned long*)src) >> 16;
					a0 = src[0];
					a1 = src[1];
					src += 8;
				}
				c0 = *(unsigned short*)(src + 0);
				c1 = *(unsigned short*)(src + 2);
				src += 4;
				
				color[0].r = ((c0 >> 11) & 0x1f) << 3;
				color[0].g = ((c0 >> 5) & 0x3f) << 2;
				color[0].b = (c0 & 0x1f) << 3;
				color[1].r = ((c1 >> 11) & 0x1f) << 3;
				color[1].g = ((c1 >> 5) & 0x3f) << 2;
				color[1].b = (c1 & 0x1f) << 3;
				if(c0 > c1) {
					color[2].r = (color[0].r * 2 + color[1].r) / 3;
					color[2].g = (color[0].g * 2 + color[1].g) / 3;
					color[2].b = (color[0].b * 2 + color[1].b) / 3;
					color[3].r = (color[0].r + color[1].r * 2) / 3;
					color[3].g = (color[0].g + color[1].g * 2) / 3;
					color[3].b = (color[0].b + color[1].b * 2) / 3;
				} else {
					color[2].r = (color[0].r + color[1].r) / 2;
					color[2].g = (color[0].g + color[1].g) / 2;
					color[2].b = (color[0].b + color[1].b) / 2;
					color[3].r = 0;
					color[3].g = 0;
					color[3].b = 0;
				}
				for(i = 0; i < 4; i++) {
					unsigned int index = *src++;
					unsigned char *dest = data + (image_width * (y + i) + x) * 4;
					for(j = 0; j < 4; j++) {
						*dest++ = color[index & 0x03].r;
						*dest++ = color[index & 0x03].g;
						*dest++ = color[index & 0x03].b;
						if(format == DDS_DXT1) {
							*dest++ = ((index & 0x03) == 3 && c0 <= c1) ? 0 : 255;
						} else if(format == DDS_DXT3) {
							*dest++ = (unsigned char)((alpha & 0x0f) << 4);
							alpha >>= 4;
						} else if(format == DDS_DXT5) {
							unsigned int a = (unsigned int)(alpha & 0x07);
							if(a == 0) *dest++ = a0;
							else if(a == 1) *dest++ = a1;
							else if(a0 > a1) *dest++ = ((8 - a) * a0 + (a - 1) * a1) / 7;
							else if(a > 5) *dest++ = (a == 6) ? 0 : 255;
							else *dest++ = ((6 - a) * a0 + (a - 1) * a1) / 5;
							alpha >>= 3;
						} else *dest++ = 255;
						index >>= 2;
					}
				}
			}
		}
		free(buf);
	}
	fclose(file);
	return data;
}

/*
=================================
		Image loading
=================================
*/

byte* loadimagepixels (char* filename, qboolean complain)
{
	FILE	*f;
	char	basename[128], name[255];
	byte	*c;

	COM_StripExtension(filename, basename); // strip the extension to allow TGA and PCX

	for (c = basename;*c;c++)
		if (*c == '*')
			*c = '#';
/*
=============================
		Try level depended textures Load
=============================
*/
	//TGA
    sprintf (name,"textures/%s/%s.tga", sv.name,basename);
    COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadTGA (f, name);
        }

	//JPG
    sprintf (name,"textures/%s/%s.jpg", sv.name,basename);
    COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadJPG (f);
        }

	//PNG
    sprintf (name,"textures/%s/%s.png", sv.name,basename);
    COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadPNG (f);
        }

	//PCX
    sprintf (name,"textures/%s/%s.pcx", sv.name,basename);
    COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadPCX (f, name);
        }

	//DDS
    sprintf (name,"textures/%s/%s.dds", sv.name,basename);
    COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadDDS (f);
        }

/*
=============================
		Try Load for old Texpack compatibility
=============================
*/
	//TGA
    sprintf (name,"textures/%s/textures/%s.tga", sv.name,basename);
    COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadTGA (f, name);
        }
	
	//JPG
    sprintf (name,"textures/%s/textures/%s.jpg", sv.name,basename);
    COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadJPG (f);
        }

	//PNG
    sprintf (name,"textures/%s/textures/%s.png", sv.name,basename);
    COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadPNG (f);
        }

	//PCX
    sprintf (name,"textures/%s/textures/%s.pcx", sv.name,basename);
    COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadPCX (f, name);
        }

	//DDS
    sprintf (name,"textures/%s/textures/%s.dds", sv.name,basename);
    COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadDDS (f);
        }

/*
=============================
		Try Load from main storage - textures folder
=============================
*/

	//TGA
	sprintf (name, "textures/%s.tga", basename);
	COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadTGA (f, name);
        }
	
	//JPG
    sprintf (name, "textures/%s.jpg", basename);
	COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadJPG (f);
        }

	//PNG
	sprintf (name, "textures/%s.png", basename);
	COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadPNG (f);
        }

	//PCX
    sprintf (name, "textures/%s.pcx", basename);
	COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadPCX (f, name);
        }
	
	//DDS
    sprintf (name, "textures/%s.dds", basename);
	COM_FOpenFile (name, &f,true);
	if (f) {
    	return LoadDDS (f);
        }

/*
=============================
		Try Load our last hope
=============================
*/
	//TGA
	sprintf (name, "%s.tga", basename);
	COM_FOpenFile (name, &f,true);
	if (f)
		{
    	return LoadTGA (f, name);
        }

	//JPG
	sprintf (name, "%s.jpg", basename);
	COM_FOpenFile (name, &f,true);
	if (f)
		{
    	return LoadJPG (f);
        }

	//PNG
	sprintf (name, "%s.png", basename);
	COM_FOpenFile (name, &f,true);
	if (f)
		{
    	return LoadPNG (f);
        }

	//PCX
	sprintf (name, "%s.pcx", basename);
	COM_FOpenFile (name, &f,true);
	if (f)
		{
    	return LoadPCX (f, name);
        }

	//DDS
	sprintf (name, "%s.dds", basename);
	COM_FOpenFile (name, &f,true);
	if (f)
		{
    	return LoadDDS (f);
        }
	
/*
=============================
		Shit happens
=============================
*/
	if (complain == true)
	    Con_Printf ("Couldn't load %s.tga or .pcx\n", filename);
	
    return NULL;
}

int loadtextureimage (char* filename, qboolean complain, qboolean mipmap, qboolean sprite)
{
	int			j, texnum;
	byte		*data;
	qboolean	transparent;

	if (isDedicated)
		return 0;

	transparent	= false;
    
	data = loadimagepixels (filename, complain);

/*
    if(sprite == true)
	    {
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        }
*/

	if(!data)
		return 0;

    //++ jes Experimental Alpha Fix
    /*
    if(sprite == true) {
        for (j = 0;j < image_width*image_height;j++)
	        {
		    data[j*4+3] = 255 - min(max(1,(data[j*4]+data[j*4+1]+data[j*4+2])-512),255);
                transparent = true;
            }
        }
    //-- jes
    */
	for (j = 0;j < image_width*image_height;j++)
	{
		if (data[j*4+3] < 255)
		{
			transparent = true;
			break;
		}
	}

	texnum = GL_LoadTexture (filename, image_width, image_height, data, mipmap, transparent,0, 4 , sprite);

	free(data);

    /*
	if(sprite == true)
        {
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
	    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
	    }
    */
	return texnum;
}

// Tomaz || Image End


/*

void Image_HeightmapToNormalmap_BGRA(const unsigned char *inpixels, unsigned char *outpixels, int width, int height, int clamp, float bumpscale)
{
        int x, y, x1, x2, y1, y2;
        const unsigned char *b, *row[3];
        int p[5];
        unsigned char *out;
        float iwidth, iheight, ibumpscale, n[3];
        iwidth = 1.0f / width;
        iheight = 1.0f / height;
        ibumpscale = (255.0f * 6.0f) / bumpscale;
        out = outpixels;
        for (y = 0, y1 = height-1;y < height;y1 = y, y++)
        {
                y2 = y + 1;if (y2 >= height) y2 = 0;
                row[0] = inpixels + (y1 * width) * 4;
                row[1] = inpixels + (y  * width) * 4;
                row[2] = inpixels + (y2 * width) * 4;
                for (x = 0, x1 = width-1;x < width;x1 = x, x++)
                {
                        x2 = x + 1;if (x2 >= width) x2 = 0;
                        // left, right
                        b = row[1] + x1 * 4;p[0] = (b[0] + b[1] + b[2]);
                        b = row[1] + x2 * 4;p[1] = (b[0] + b[1] + b[2]);
                        // above, below
                        b = row[0] + x  * 4;p[2] = (b[0] + b[1] + b[2]);
                        b = row[2] + x  * 4;p[3] = (b[0] + b[1] + b[2]);
                        // center
                        b = row[1] + x  * 4;p[4] = (b[0] + b[1] + b[2]);
                        // calculate a normal from the slopes
                        n[0] = p[0] - p[1];
                        n[1] = p[3] - p[2];
                        n[2] = ibumpscale;
                        VectorNormalize(n);
                        // turn it into a dot3 rgb vector texture
                        out[2] = (int)(128.0f + n[0] * 127.0f);
                        out[1] = (int)(128.0f + n[1] * 127.0f);
                        out[0] = (int)(128.0f + n[2] * 127.0f);
                        out[3] = (p[4]) / 3;
                        out += 4;
                }
        }
}

*/