///////////////////////////////////////////////////////////////////////////////
/// @file deBitmap.cpp
///
/// @brief bitmap processing and conversion
///
/// @author Assassin, Hootie, paradoxnj
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Jan 2002
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#include <cmath>
#include "deBitmap_priv.hpp"
#include "deFileSystem.hpp"
#include "deMath.hpp"
#include "deFile.hpp"

WorldObjectClassImp(deBitmapProxy);

long deBitmap::s_BitmapRscInterfaceID = 0;
long deBitmapProxy::s_BitmapProxyWOInterfaceID = -1;

static IdeFile * LoadFile(const char * filename, IdeFileSystem* BaseFS, deBoolean Read, deBoolean Write)
{
	IdeFile *		DataFile = NULL;
	long			OpenFlags;

	if (BaseFS == NULL)
	{
		BaseFS = IdeFileSystem_CreateFSReal();
		if (!BaseFS)
			return NULL;
	}
	else
	{
//#pragma todo (handle loading of files from inside another filesystem)
		BaseFS->Claim();
	}

	OpenFlags = 0;
	if (Read && Write)
		OpenFlags = deFile_Open_Always | deFile_Open_Read | deFile_Open_Write;
	else if (Read)
		OpenFlags = deFile_Open_Existing | deFile_Open_Read;
	else if (Write)
		OpenFlags = deFile_Open_CreateAlways | deFile_Open_Write;
	
	DataFile = BaseFS->Open(filename, OpenFlags);
	BaseFS->Release();
	if (!DataFile)
	{
		DEBUGMSG1("LoadFile: could not open file '%s'", filename);
		return NULL;
	}

	return DataFile;
}

IdeResourceBase *LoadBMP(const char *filename, IdeFile *file, deBoolean /*isThreaded*/)
{
	deBitmap	*bmp;
	
	if (file == NULL)
		return NULL;
	
	// create a bitmap with the specified filename
	bmp = new deBitmap(filename);
	if (!bmp)
		return NULL;
	// load the bitmap data out of the file
	if (!bmp->LoadBMPFile(file))
	{
		bmp->Release();
		bmp = NULL;
	}

	return bmp;
}

IdeResourceBase *LoadTGA(const char *filename, IdeFile *file, deBoolean /*isThreaded*/)
{
	deBitmap	*bmp;
	
	if (file == NULL)
		return NULL;
	
	// create a bitmap with the specified filename
	bmp = new deBitmap(filename);
	if (!bmp)
		return NULL;
	// load the bitmap data out of the file
	if (!bmp->LoadTGAFile(file))
	{
		bmp->Release();
		bmp = NULL;
	}
	
	return bmp;
}

IdeResourceBase *LoadPCX(const char *filename, IdeFile *file, deBoolean /*isThreaded*/)
{
	deBitmap	*bmp;
	
	if (file == NULL)
		return NULL;
	
	// create a bitmap with the specified filename
	bmp = new deBitmap(filename);
	if (!bmp)
		return NULL;
	// load the bitmap data out of the file
	if (!bmp->LoadPCXFile(file))
	{
		bmp->Release();
		bmp = NULL;
	}
	
	return bmp;
}

IdeResourceBase *LoadPNG(const char *filename, IdeFile *file, deBoolean /*isThreaded*/)
{
	deBitmap	*bmp;

	if (file == NULL)
		return NULL;
	
	// create a bitmap with the specified filename
	bmp = new deBitmap(filename);
	if (!bmp)
		return NULL;
	// load the bitmap data out of the file
	if (!bmp->LoadPNGFile(file))
	{
		bmp->Release();
		bmp = NULL;
	}
	
	return bmp;
}

IdeResourceBase *LoadDDS(const char *filename, IdeFile *file, deBoolean /*isThreaded*/)
{
	deBitmap	*bmp;

	if (file == NULL)
		return NULL;
	
	// create a bitmap with the specified filename
	bmp = new deBitmap(filename);
	if (!bmp)
		return NULL;
	// load the bitmap data out of the file
	if (!bmp->LoadDDSFile(file))
	{
		bmp->Release();
		bmp = NULL;
	}
	
	return bmp;
}

IdeResourceBase *LoadCMAP(const char *filename, IdeFile *file, deBoolean /*isThreaded*/)
{
	deBitmap	*bmp;

	if (file == NULL)
		return NULL;
	
	// create a bitmap with the specified filename
	bmp = new deBitmap(filename);
	if (!bmp)
		return NULL;
	// load the bitmap data out of the file
	if (!bmp->LoadCMAPFile(file, filename))
	{
		bmp->Release();
		bmp = NULL;
	}
	
	return bmp;
}

long BPPByteDepth(IdeDriver::BPP BPPType)
{
	switch (BPPType)
	{
	case IdeDriver::BPP_A8:
	case IdeDriver::BPP_R3G3B2:
	case IdeDriver::BPP_P8:
	case IdeDriver::BPP_L8:
	case IdeDriver::BPP_A4L4:
		return 1;

	case IdeDriver::BPP_X1R5G5B5:
	case IdeDriver::BPP_A1R5G5B5:
	case IdeDriver::BPP_A4R4G4B4:
	case IdeDriver::BPP_X4R4G4B4:
	case IdeDriver::BPP_A8R3G3B2:
	case IdeDriver::BPP_R5G6B5:
	case IdeDriver::BPP_A8P8:
	case IdeDriver::BPP_A8L8:
	case IdeDriver::BPP_V8U8:
	case IdeDriver::BPP_L6V5U5:
		return 2;

	case IdeDriver::BPP_R8G8B8:
		return 3;

	case IdeDriver::BPP_A2B10G10R10:
	case IdeDriver::BPP_G16R16:
	case IdeDriver::BPP_A8R8G8B8:
	case IdeDriver::BPP_X8R8G8B8:
	case IdeDriver::BPP_Q8W8V8U8:
	case IdeDriver::BPP_V16U16:
	case IdeDriver::BPP_W11V11U10:
	case IdeDriver::BPP_X8L8V8U8:
	case IdeDriver::BPP_A2W10V10U10:
		return 4;

	// floating-point formats
	case IdeDriver::BPP_R16F:
		return 2;
		
	case IdeDriver::BPP_G16R16F:
	case IdeDriver::BPP_R32F:
		return 4;
		
	case IdeDriver::BPP_A16B16G16R16F:
	case IdeDriver::BPP_G32R32F:
		return 8;
		
	case IdeDriver::BPP_A32B32G32R32F:
		return 16;

	case IdeDriver::BPP_UYVY:
	case IdeDriver::BPP_YUY2:
	case IdeDriver::BPP_DXT1:
	case IdeDriver::BPP_DXT2:
	case IdeDriver::BPP_DXT3:
	case IdeDriver::BPP_DXT4:
	case IdeDriver::BPP_DXT5:
		
	default:
		return 0;
	}
}

deBoolean IdeBitmap_Initialize()
{
	// initialize all the file extensions we know how to load
	IdeResourceManager * RscManager = IdeResourceManager_GetManager();
	if (!RscManager)
		DEBUGRETURN(deFALSE, "IdeBitmap_Initialize: invalid resource manager");
	deBitmap::s_BitmapRscInterfaceID = RscManager->GetUniqueInterfaceID();
	RscManager->RegisterResourceType("bmp", LoadBMP);
	RscManager->RegisterResourceType("tga", LoadTGA);
	RscManager->RegisterResourceType("pcx", LoadPCX);
	RscManager->RegisterResourceType("png", LoadPNG);
	RscManager->RegisterResourceType("dds", LoadDDS);	// directdrawsurface file format
	RscManager->RegisterResourceType("cmap", LoadCMAP); // cubemap text file format
	
	IdeWorldManager * wmanager = IdeWorld_GetManager();
	if (!wmanager)
		DEBUGRETURN(deFALSE, "IdeBitmap_Initialize: invalid world manager");
	deBitmapProxy::s_BitmapProxyWOInterfaceID = wmanager->GetUniqueWOInterfaceID();
	if (!WorldObjectRegister(deBitmapProxy))
		DEBUGRETURN(deFALSE, "IdeBitmap_Initialize: deBitmapProxy");

	return deTRUE;
}

long IdeBitmap_GetRscInterfaceID()
{
	// returns our statically-held unique identifier
	return deBitmap::s_BitmapRscInterfaceID;
}

long IdeBitmapProxy_GetWOInterfaceID()
{
	// returns our statically-held unique identifier
	return deBitmapProxy::s_BitmapProxyWOInterfaceID;
}

IdeBitmap* IdeBitmap_CreateBitmap()
{
	// fairly straightforward here... make a bitmap with no name
	return new deBitmap;
}

IdeBitmapProxy* IdeBitmap_CreateProxy(IdeResourceBase* FromBitmap)
{
	IdeBitmap* bmp;
	if (FromBitmap)
	{
		bmp = (IdeBitmap*)FromBitmap->GetRscInterface(deBitmap::s_BitmapRscInterfaceID);
		if (!bmp)
			return NULL;
	}
	else
		return new deBitmapProxy;
	// fairly straightforward here... make a bitmap with no name
	return new deBitmapProxy(bmp);
}

IdeBitmap* IdeBitmap_CreateBitmapWithInfo(long Width, long Height, IdeDriver::BPP Format, deBoolean CubeMap)
{
	// create a bitmap with the specified information
	deBitmap * bmp = new deBitmap;
	int bytedepth = BPPByteDepth(Format);

	// we figure out how much space is needed from the width*height*bitdepth
	int imagesize = Width * Height * bytedepth;

	// make sure no one is playing with our sizes
	// also make sure imagesize does not overflow
	if((Width <= 0) || (Height <= 0) || (!bytedepth) || (imagesize <= 0))
		return NULL;

	BYTE* data = new BYTE[imagesize];
	if (!data)
		return NULL;

	// put the data into the bitmap itself
	bmp->SetWidth(Width);
	bmp->SetHeight(Height);
	bmp->SetFormat(Format);
	bmp->SetData(data, 0, 0);

	if (CubeMap)
	{
		int i;
		for (i = 1; i < 6; ++i)
		{
			BYTE* data = new BYTE[imagesize];
			bmp->SetData(data, 0, i);
		}
	}

	return bmp;
}

deBitmap::deBitmap()
:	m_Data(1, NULL)
// no filename
{
	Reset();
}

deBitmap::deBitmap(const char *filename)
:	deResourceBase(filename),
	m_Data(1, NULL)
{
	Reset();
}

deBitmap::~deBitmap()
{
	Cleanup();
}

void * deBitmap::GetRscInterface(long interface_id)
{
	// most expected query: IdeBitmap
	if (interface_id == s_BitmapRscInterfaceID)
	{
		return (IdeBitmap*)(this);
	}
	// least expected query: IdeResourceBase
	if (interface_id == 0)
	{
		return (IdeResourceBase*)(this);
	}
	// unknown interface query, return NULL
	return NULL;
}

deBoolean deBitmap::Reset()
{
	// wipe out all our data and free up all memory
	int k, size;
	size = m_Data.size();
	for (k = 0; k < size; ++k)
	{
		delete[] m_Data[k];
		m_Data[k] = NULL;
	}
	m_Height = m_Width = 0;
	m_ByteDepth = 0;
	m_ImageSize = 0;
	m_RenderTarget = deFALSE;
	m_Format = IdeDriver::BPP_A8R8G8B8;
	m_MipLevels = -1;

	return deTRUE;
}

void deBitmap::Cleanup()
{
	DriverBitmapEntry * Entry;
	DriverBitmapEntry * Entry2;
	void * ptr;

	// cycle through all the drivers that we've been entered into
	// and tell them to remove us
	ptr = m_DriverData.GetNext(NULL, Entry2);
	while (ptr)
	{
		// cache the next entry
		// because Driver->RemoveBitmap will invoke this->SetDriverData
		// which will remove it from the list
		Entry = Entry2;
		ptr = m_DriverData.GetNext(ptr, Entry2);

		if (Entry->Driver)
		{
			Entry->Driver->RemoveBitmap(this);
		}
	}

	int k, size;
	size = m_Data.size();
	for (k = 0; k < size; ++k)
	{
		delete[] m_Data[k];
		m_Data[k] = NULL;
	}
}

void* deBitmap::GetData(u32 FaceNum)
{
	if (FaceNum >= m_Data.size())
		DEBUGRETURN(NULL, "GetData: Invalid FaceNum");
	return m_Data[FaceNum];
}

deBoolean deBitmap::SaveToFileSystem(IdeFileSystem *FS, const char *filename)
{
	if (!filename)
		DEBUGRETURN(deFALSE, "SaveToFile: Parameter 'filename' was NULL");

	// we can only save to .bmp files for the time being
	// might add .png support at some point
	const char* ext = ".bmp";
	char * str;
	int length;
	length = strlen(filename);
	// test whether the filename already has the ".bmp" specified
	if (length <= 4 || 0 != strcmpi(filename+(length-4), ext))
	{
		// if not, make space for a string with enough space for it to be added
		str = new char[length+4+1];
		strcpy(str, filename);
		strcat(str, ext);
	}
	else
	{
		// if so, just copy the string
		str = (char*)filename;
	}

	// create a file to write into with the filename requested
	IdeFile* NewFile;
	NewFile = LoadFile(str, FS, deFALSE, deTRUE);
	if (!NewFile)
	{
		if (str != filename)
			delete[] str;
		return deFALSE;
	}

	// save the bitmap data
	deBoolean Result = SaveToFile(NewFile);

	// close the file
	NewFile->Close();
	NewFile->Release();
	NewFile = NULL;

	// yes, we are comparing pointers - if the ".bmp" was appended, the string needs deleting
	if (str != filename)
		delete[] str;

	// hopefully success
	return Result;
}

IdeResourceBase *deBitmap::MakeCopy()
{
	// create a copy of this object
	deBitmap		*bmp;

	bmp = new deBitmap;
	if (!bmp)
		return NULL;

	*bmp = *this;
	if (m_ImageSize > 0)
	{
		int k, size;
		size = m_Data.size();
		for (k = 0; k < size; ++k)
		{
			if (m_Data[k])
			{
				bmp->m_Data[k] = new BYTE[m_ImageSize];
				memcpy(bmp->m_Data[k], m_Data[k], m_ImageSize);
			}
			else
			{
				bmp->m_Data[k] = NULL;
			}
		}
	}
	bmp->m_DriverData.EmptyList();

	return bmp;
}

void* deBitmap::GetDriverData(long ID)
{
	// return the data that the driver previously registered with this bitmap
	DriverBitmapEntry * Entry;
	void * ptr;

	ptr = m_DriverData.GetNext(NULL, Entry);
	while (ptr)
	{
		if (Entry->ID == ID)
			break;

		ptr = m_DriverData.GetNext(ptr, Entry);
	}
	
	if (!ptr)
		return NULL;

	return Entry->Data;
}

deBoolean deBitmap::SetDriverData(long ID, void *Data, IdeDriver * Driver)
{
	// store data that a driver is registering with this bitmap
	DriverBitmapEntry * Entry;
	void * ptr;
	
	// find a previously registered entry
	ptr = m_DriverData.GetNext(NULL, Entry);
	while (ptr)
	{
		if (Entry->ID == ID)
		{
			DE_ASSERT(Entry->Driver == Driver);
			break;
		}
		
		ptr = m_DriverData.GetNext(ptr, Entry);
	}
	
	if (!ptr)
	{
		// if it didn't previously register, create an entry and store into that
		DriverBitmapEntry NewEntry;
		NewEntry.ID = ID;
		NewEntry.Data = Data;
		NewEntry.Driver = Driver;
		if (!m_DriverData.AddElement(NewEntry))
			return deFALSE;
	}
	else
	{
		// otherwise just store
		if (Data)
		{
			Entry->Data = Data;
		}
		else
		{
			if (!m_DriverData.RemoveElement(ptr))
				return deFALSE;
		}
	}

	// success
	return deTRUE;
}

deARGB deBitmap::GetColorAt(long X, long Y)
{
	// grab a color at a certain pixel location
	// we can read 32-bit raw data directly into the return value
	if (m_Format == IdeDriver::BPP_A8R8G8B8)
	{
		return ((deARGB*)m_Data[0])[WrappedIndex(X,Y)];
	}
	if (m_Format == IdeDriver::BPP_X8R8G8B8)
	{
		return ((deARGB*)m_Data[0])[WrappedIndex(X,Y)] | 0xff000000;
	}
	// other formats must be processed
	deColor AccurateColor;
	GetFloatColorAt(X, Y, AccurateColor);
	return deMake_Color(AccurateColor);
}

void deBitmap::GetFloatColorAt(long X, long Y, deColor & color)
{
	// grab a color at a certain pixel location
	long index = WrappedIndex(X,Y);
	GetColorIn(&(m_Data[0][index*m_ByteDepth]), m_Format, color);
}

void deBitmap::SetColorAt(long X, long Y, deARGB color)
{
	// put a color at a certain pixel location
	// we can set 32-bit raw data directly into the buffer
	if (m_Format == IdeDriver::BPP_A8R8G8B8 || m_Format == IdeDriver::BPP_X8R8G8B8)
	{
		*((deARGB*)(&m_Data[0][WrappedIndex(X,Y) * m_ByteDepth])) = color;
	}
	// other formats must be processed
	long index = WrappedIndex(X,Y);
	deColor AccurateColor = deMake_Color(color);
	SetColorIn(&(m_Data[0][index*m_ByteDepth]), m_Format, AccurateColor);
}

void deBitmap::SetColorAt(long X, long Y, const deColor & color)
{
	// put a color at a certain pixel location
	long index = WrappedIndex(X,Y);
	SetColorIn(&(m_Data[0][index*m_ByteDepth]), m_Format, color);
}

void GetColorIn(unsigned char* data, IdeDriver::BPP Format, deColor &RetColor)
{
	// get the floating-point color from a piece of data (hopefully a pixel)
	if (Format == IdeDriver::BPP_A8R8G8B8 || Format == IdeDriver::BPP_X8R8G8B8)
	{
		if (DE3D_CPU_LITTLE_ENDIAN)
		{
			RetColor.a = *(data + 3) / 255.0f;
			RetColor.r = *(data + 2) / 255.0f;
			RetColor.g = *(data + 1) / 255.0f;
			RetColor.b = *(data    ) / 255.0f;
		}
		else
		{
			RetColor.a = *(data    ) / 255.0f;
			RetColor.r = *(data + 1) / 255.0f;
			RetColor.g = *(data + 2) / 255.0f;
			RetColor.b = *(data + 3) / 255.0f;
		}
	}
	else if (Format == IdeDriver::BPP_R8G8B8)
	{
		if (DE3D_CPU_LITTLE_ENDIAN)
		{
			RetColor.a = 1.0f;
			RetColor.r = *(data + 2) / 255.0f;
			RetColor.g = *(data + 1) / 255.0f;
			RetColor.b = *(data    ) / 255.0f;
		}
		else
		{
			RetColor.a = 1.0f;
			RetColor.r = *(data    ) / 255.0f;
			RetColor.g = *(data + 1) / 255.0f;
			RetColor.b = *(data + 2) / 255.0f;
		}
	}
	else if (Format == IdeDriver::BPP_R5G6B5)
	{
		u16* pPixel = (u16*)(data);
		RetColor.a = 1.0f;
		RetColor.r = ( (*pPixel >> 11) & 0x1f) / 31.0f;
		RetColor.g = ( (*pPixel >>  5) & 0x3f) / 63.0f;
		RetColor.b = ( (*pPixel      ) & 0x1f) / 31.0f;
	}
	else if (Format == IdeDriver::BPP_A4R4G4B4 || Format == IdeDriver::BPP_X4R4G4B4)
	{
		u16* pPixel = (u16*)(data);
		RetColor.a = ( (*pPixel >> 12) & 0x0f) / 15.0f;
		RetColor.r = ( (*pPixel >>  8) & 0x0f) / 15.0f;
		RetColor.g = ( (*pPixel >>  4) & 0x0f) / 15.0f;
		RetColor.b = ( (*pPixel      ) & 0x0f) / 15.0f;
	}	
	else if (Format == IdeDriver::BPP_X1R5G5B5 || Format == IdeDriver::BPP_A1R5G5B5)
	{
		u16* pPixel = (u16*)(data);
		RetColor.a = ( (*pPixel >> 15) & 0x01) /  1.0f;
		RetColor.r = ( (*pPixel >> 10) & 0x1f) / 31.0f;
		RetColor.g = ( (*pPixel >>  5) & 0x1f) / 31.0f;
		RetColor.b = ( (*pPixel      ) & 0x1f) / 31.0f;
	}
	else if (Format == IdeDriver::BPP_L8)
	{
		RetColor.a = 1.0f;
		RetColor.r = (*data) / 255.0f;
		RetColor.g = RetColor.r;
		RetColor.b = RetColor.r;
	}
	else if (Format == IdeDriver::BPP_A8)
	{
		RetColor.a = (*data) / 255.0f;
		RetColor.r = 1.0f;
		RetColor.g = RetColor.r;
		RetColor.b = RetColor.r;
	}
	else
	{
		DE_ASSERT(!"GetColorIn: Invalid format to get a color from");
	}
}

void SetColorIn(unsigned char* data, IdeDriver::BPP Format, deColor color)
{
	// set the floating-point color in a piece of data (hopefully a pixel)
	if (Format == IdeDriver::BPP_A8R8G8B8 || Format == IdeDriver::BPP_X8R8G8B8)
	{
		if (DE3D_CPU_LITTLE_ENDIAN)
		{
			data[3] = (u8)(color.a * 255);
			data[2] = (u8)(color.r * 255);
			data[1] = (u8)(color.g * 255);
			data[0] = (u8)(color.b * 255);
		}
		else
		{
			data[0] = (u8)(color.a * 255);
			data[1] = (u8)(color.r * 255);
			data[2] = (u8)(color.g * 255);
			data[3] = (u8)(color.b * 255);
		}
	}
	else if (Format == IdeDriver::BPP_R8G8B8)
	{
		if (DE3D_CPU_LITTLE_ENDIAN)
		{
			data[2] = (u8)(color.r * 255);
			data[1] = (u8)(color.g * 255);
			data[0] = (u8)(color.b * 255);
		}
		else
		{
			data[0] = (u8)(color.r * 255);
			data[1] = (u8)(color.g * 255);
			data[2] = (u8)(color.b * 255);
		}
	}
	else if (Format == IdeDriver::BPP_R5G6B5)
	{
		u16* pPixel = (u16*)(data);
		*pPixel  = (u16)((u16)(color.r * 31) << 11);
		*pPixel |=      ((u16)(color.g * 63) <<  5);
		*pPixel |=      ((u16)(color.b * 31)      );
	}	
	else if (Format == IdeDriver::BPP_A4R4G4B4 || Format == IdeDriver::BPP_X4R4G4B4)
	{
		u16* pPixel = (u16*)(data);
		*pPixel  = (u16)((u16)(color.a * 15) << 12);
		*pPixel |=      ((u16)(color.r * 15) <<  8);
		*pPixel |=      ((u16)(color.g * 15) <<  4);
		*pPixel |=      ((u16)(color.b * 15)      );
	}	
	else if (Format == IdeDriver::BPP_X1R5G5B5 || Format == IdeDriver::BPP_A1R5G5B5)
	{
		u16* pPixel = (u16*)(data);
		*pPixel  = (u16)((u16)(color.a *  1) << 15);
		*pPixel |=      ((u16)(color.r * 31) << 10);
		*pPixel |=      ((u16)(color.g * 31) <<  5);
		*pPixel |=      ((u16)(color.b * 31)      );
	}	
	else if (Format == IdeDriver::BPP_L8)
	{
		*data = (unsigned char)(((color.r + color.g + color.b)/3) * 255);
	}
	else if (Format == IdeDriver::BPP_A8)
	{
		*data = (unsigned char)(color.a * 255);
	}
	else
	{
		DE_ASSERT(!"GetColorIn: Invalid format to set a color in");
	}
}

long deBitmap::WrappedIndex(long X, long Y)
{
	// find the wrapped index of a pixel in our data buffer
	long index = 0;

	X &= 0x7fffffff;
	Y &= 0x7fffffff;

	index = (X%m_Width) + (Y%m_Height)*m_Width;

	return index;
}

long deBitmap::WrappedIndex(long X, long Y, long Width, long Height)
{
	// find the wrapped index of a pixel in an arbitrary data buffer
	long index = 0;
	
	X &= 0x7fffffff;
	Y &= 0x7fffffff;
	
	index = (X%Width) + (Y%Height)*Width;
	
	return index;
}

deBoolean deBitmap::ConvertGrayScaleToDot3(long numsamples, deFloat amplify)
{
	// ripped off from the direct3d dot-product 3 sample
	// but then extended significantly for higher quality output (more samples)

	if (m_Data.size() > 1)
		DEBUGRETURN(deFALSE, "ConvertGrayScaleToDot3 cannot be executed on non-flat textures");

	int			i, j;
	deBoolean	UseAlpha;
	DWORD*		pPixel;

	amplify += 1.0;

	// this can be kinda fickle... but it can read heightmap info out of alpha channel
	if (m_Format == IdeDriver::BPP_A8R8G8B8)
		UseAlpha = deTRUE;
	else
		UseAlpha = deFALSE;

	pPixel = (DWORD*)(new BYTE[4*m_Width*m_Height]);

	// For each pixel, generate a vector normal that represents the change
	// in thea height field at that pixel
	if (numsamples == 3)
	{
		DWORD	color00, color10, color01;
		deFloat height00, height10, height01;
		deVec3d normal;

		for (j = 0; j < m_Height; j++  )
		{
			for (i = 0; i < m_Width; i++ )
			{
				color00 = GetColorAt(i,   j  );
				color10 = GetColorAt(i+1, j  );
				color01 = GetColorAt(i,   j+1);

				if (UseAlpha)
				{
					// alpha channel
					height00 = (deFloat((color00 & 0xff000000)>>24))/255.0f;
					height10 = (deFloat((color10 & 0xff000000)>>24))/255.0f;
					height01 = (deFloat((color01 & 0xff000000)>>24))/255.0f;
				}
				else
				{
					// blue channel
					height00 = (deFloat(color00 & 0x000000ff))/255.0f;
					height10 = (deFloat(color10 & 0x000000ff))/255.0f;
					height01 = (deFloat(color01 & 0x000000ff))/255.0f;
				}

				// compute the "face normal" of the triangle made by the 3 pixel-height samples
				normal = deMake_Vec3d(height10-height00, height01-height00, 1);
				normal.x *= amplify;
				normal.y *= amplify;

				Vec3d_Normalize( normal );

				// Store the normal as an RGBA value in the normal map
				pPixel[WrappedIndex(i, j)] = Vec3d_ConvertToDot3( normal, height00/amplify );
			}
		}
	}
	else if (numsamples >= 2 && numsamples <= 9)
	{
		deARGB	color;
		deFloat heightX, heightY, MidHeight = 0;
		deVec3d normal;
		deBoolean GetCenter;

		GetCenter = (deBoolean)(numsamples%2);

		for (j = 0; j < m_Height; j++  )
		{
			for (i = 0; i < m_Width; i++ )
			{
				heightX = 0;
				heightY = 0;

				if (UseAlpha)
				{
					if (GetCenter)
					{
						color = GetColorAt(i, j);
						MidHeight = (deFloat((color & 0xff000000)>>24))/255.0f;
					}

					// if it's not 4 or 5 ( can be 2,3,6,7)
					if (numsamples % 4 > 1)
					{
						color = GetColorAt(i-1, j);
						heightX += MidHeight - (deFloat((color & 0xff000000)>>24))/255.0f;

						color = GetColorAt(i+1, j);
						heightX += (deFloat((color & 0xff000000)>>24))/255.0f - MidHeight;

						color = GetColorAt(i, j-1);
						heightY += MidHeight - (deFloat((color & 0xff000000)>>24))/255.0f;

						color = GetColorAt(i, j+1);
						heightY += (deFloat((color & 0xff000000)>>24))/255.0f - MidHeight;
					}

					if (numsamples >= 4)
					{
						color = GetColorAt(i-1, j-1);
						heightX += MidHeight - (deFloat((color & 0xff000000)>>24))/255.0f;
						heightY += MidHeight - (deFloat((color & 0xff000000)>>24))/255.0f;

						color = GetColorAt(i+1, j-1);
						heightX += (deFloat((color & 0xff000000)>>24))/255.0f - MidHeight;
						heightY += MidHeight - (deFloat((color & 0xff000000)>>24))/255.0f;

						color = GetColorAt(i-1, j+1);
						heightX += MidHeight - (deFloat((color & 0xff000000)>>24))/255.0f;
						heightY += (deFloat((color & 0xff000000)>>24))/255.0f - MidHeight;

						color = GetColorAt(i+1, j+1);
						heightX += (deFloat((color & 0xff000000)>>24))/255.0f - MidHeight;
						heightY += (deFloat((color & 0xff000000)>>24))/255.0f - MidHeight;
					}

					if (numsamples >= 8)
					{
						color = GetColorAt(i-1, j);
						heightY += MidHeight - (deFloat((color & 0xff000000)>>24))/255.0f;

						color = GetColorAt(i+1, j);
						heightY += (deFloat((color & 0xff000000)>>24))/255.0f - MidHeight;

						color = GetColorAt(i, j-1);
						heightX += MidHeight - (deFloat((color & 0xff000000)>>24))/255.0f;

						color = GetColorAt(i, j+1);
						heightX += (deFloat((color & 0xff000000)>>24))/255.0f - MidHeight;
					}
				}
				else
				{
					if (GetCenter)
					{
						color = GetColorAt(i, j);
						MidHeight = (deFloat((color & 0x00ff0000)>>16))/255.0f;
					}

					if (numsamples % 4 > 1)
					{
						color = GetColorAt(i-1, j);
						heightX += MidHeight - (deFloat((color & 0x00ff)))/255.0f;

						color = GetColorAt(i+1, j);
						heightX += (deFloat((color & 0x00ff)))/255.0f - MidHeight;

						color = GetColorAt(i, j-1);
						heightY += MidHeight - (deFloat((color & 0x00ff)))/255.0f;

						color = GetColorAt(i, j+1);
						heightY += (deFloat((color & 0x00ff)))/255.0f - MidHeight;
					}

					if (numsamples >= 4)
					{
						color = GetColorAt(i-1, j-1);
						heightX += MidHeight - (deFloat((color & 0x00ff)))/255.0f;
						heightY += MidHeight - (deFloat((color & 0x00ff)))/255.0f;

						color = GetColorAt(i+1, j-1);
						heightX += (deFloat((color & 0x00ff)))/255.0f - MidHeight;
						heightY += MidHeight - (deFloat((color & 0x00ff)))/255.0f;

						color = GetColorAt(i-1, j+1);
						heightX += MidHeight - (deFloat((color & 0x00ff)))/255.0f;
						heightY += (deFloat((color & 0x00ff)))/255.0f - MidHeight;

						color = GetColorAt(i+1, j+1);
						heightX += (deFloat((color & 0x00ff)))/255.0f - MidHeight;
						heightY += (deFloat((color & 0x00ff)))/255.0f - MidHeight;
					}

					if (numsamples >= 8)
					{
						color = GetColorAt(i-1, j);
						heightY += MidHeight - (deFloat((color & 0x00ff)))/255.0f;

						color = GetColorAt(i+1, j);
						heightY += (deFloat((color & 0x00ff)))/255.0f - MidHeight;

						color = GetColorAt(i, j-1);
						heightX += MidHeight - (deFloat((color & 0x00ff)))/255.0f;

						color = GetColorAt(i, j+1);
						heightX += (deFloat((color & 0x00ff)))/255.0f - MidHeight;
					}
				}

				normal.x = (heightX) * amplify;
				normal.y = (heightY) * amplify;
				normal.z = 1.0;

				Vec3d_Normalize( normal );

				// Store the normal as an RGBA value in the normal map
				pPixel[WrappedIndex(i, j)] = Vec3d_ConvertToDot3( normal, MidHeight/amplify );
			}
		}
	}
	else
	{
		delete[] pPixel;
		return deFALSE;
	}

	SetData(pPixel, 0, 0);
	SetFormat(IdeDriver::BPP_A8R8G8B8);

    return deTRUE;
}

deBoolean deBitmap::BindGrayScaleAsAlphaMap(IdeBitmap *AlphaMap)
{
	// binds a grayscale image into the alpha channel of this one
	// it doesn't need to be grayscale - a basic luminance value is computed
	int x;

	if (AlphaMap == NULL)
		DEBUGRETURN(deFALSE, "BindGrayScaleAsAlphaMap: Specified parameter was NULL: AlphaMap");
	if ((x= AlphaMap->GetWidth()) != m_Width)
		DEBUGRETURN(deFALSE, "BindGrayScaleAsAlphaMap: Alpha map width doesn't match");
	if ((x= AlphaMap->GetHeight()) != m_Height)
		DEBUGRETURN(deFALSE, "BindGrayScaleAsAlphaMap: Alpha map height doesn't match");

	deARGB		SrcColor, DestColor;
	int			y;
	deDouble	AlphaVal;

	// if we're a non-alpha bitmap... we just got alpha
	if (m_Format == IdeDriver::BPP_X8R8G8B8)
		m_Format = IdeDriver::BPP_A8R8G8B8;

	for (y = 0; y < m_Height; ++y)
	{
		for (x = 0; x < m_Width; ++x)
		{
			SrcColor = AlphaMap->GetColorAt(x,y);
			DestColor = this->GetColorAt(x,y);
			// they say it's grayscale, but we'll take a luminance value anyway
			AlphaVal  = ((SrcColor & 0x00ff0000) >>16);
			AlphaVal += ((SrcColor & 0x0000ff00) >> 8);
			AlphaVal += ((SrcColor & 0x000000ff) >> 0);
			AlphaVal /= 3.0;
			// round off the grayscale value and put it into the alpha component
			DestColor = (DestColor & 0x00ffffff) | ( ((unsigned long)(AlphaVal+0.5) & 0x000000ff) << 24);
			this->SetColorAt(x,y,DestColor);
		}
	}
	return deTRUE;
}

deBoolean deBitmap::DownSampleDXTC(void* &source, void* target,
								   long SP, long SW, long SH, long TP, long TH, 
								   IdeDriver::BPP SFormat, IdeDriver::BPP TFormat)
{
	// this is sort of a hack, but it works
	// DXTC mip-maps are stored in memory, and we can just go from the first to the last to build our mip-chain
	// the way we tell if we're reading a valid one is to test whether the pointer is in our memory space
	// note that DXTC gets a 4:1 compression over normal bitmap data, but stores it all funky for hardware usage
	BYTE* bytesource = (BYTE*)source;
	long srcsize;
	srcsize = SP*(max(SH/4,1));
	if (SFormat == TFormat)
	{
		long destsize = TP*(max(TH/4,1));
		u8* base = m_Data[0];
		if (bytesource<base || bytesource+srcsize+destsize > base+m_ImageSize)
			DEBUGRETURN(deFALSE, "DownSampleDXTC: attempt to grab a MIP from a non-mipped DXTC texture");
		memcpy(target, bytesource+srcsize, destsize);
	}
	else
	{
		if (!DecompressDXTC(source, target, SW, SH, TP, SFormat, TFormat))
			return deFALSE;
	}
	source = bytesource+srcsize;
	return deTRUE;
}

deBoolean deBitmap::DownSampleMIP(void* &source, void* target, 
								  long SW, long SH, long SP, 
								  long TW, long TH, long TP, 
								  IdeDriver::BPP SFormat, IdeDriver::BPP TFormat)
{
	deColor	color1,color2;
	long		SByteDepth, TByteDepth;
	long		x,y, index, xcoeff, ycoeff, xsample, ysample;
	float	invnumsamples;
	unsigned char* SourceData;
	unsigned char* TargetData;

	if (TW <= 0 || TH <= 0)
		DEBUGRETURN(deFALSE,"DownSampleMIP: invalid target dimensions");

	// if we're dealing with a DXTC texture, then we just read a mip-map out of memory
	if (SFormat >= IdeDriver::BPP_DXT1 && SFormat <= IdeDriver::BPP_DXT5)
	{
		// we use a custom method to do that
		return DownSampleDXTC(source, target, SP,SW,SH, TP,TH, SFormat,TFormat);
	}

	// compute the number of samples taken from source to make 1 target pixel
	// this way, we can do conversions other than 2:1 in both dimensions
	xcoeff = SW/TW;
	ycoeff = SH/TH;
	invnumsamples = 1.0f/(xcoeff*ycoeff);

	SByteDepth = BPPByteDepth(SFormat);
	TByteDepth = BPPByteDepth(TFormat);
	
	if (SByteDepth <= 0)
	{
		DEBUGMSG1("source format: %d", SFormat);
		DEBUGRETURN(deFALSE,"DownSampleMIP: invalid source bytedepth");
	}
	if (TByteDepth <= 0)
	{
		DEBUGMSG1("target format: %d", SFormat);
		DEBUGRETURN(deFALSE,"DownSampleMIP: invalid target bytedepth");
	}
	
	for (y = 0; y < TH; ++y)
	{
		for (x = 0; x < TW; ++x)
		{
			color2.a = 0;
			color2.r = 0;
			color2.g = 0;
			color2.b = 0;

			for (ysample = 0; ysample < ycoeff; ++ysample)
			{
				for (xsample = 0; xsample < xcoeff; ++xsample)
				{
					index = (x*xcoeff+xsample) % SW;
					index = WrappedIndex(index, y*ycoeff+ysample, SP/SByteDepth, SH);
					SourceData = &((unsigned char*)source)[index*SByteDepth];
					GetColorIn(SourceData, SFormat, color1);
					color2.a += color1.a;
					color2.r += color1.r;
					color2.g += color1.g;
					color2.b += color1.b;
				}
			}
			
			color2.a *= invnumsamples;
			color2.r *= invnumsamples;
			color2.g *= invnumsamples;
			color2.b *= invnumsamples;

			// test to make mip levels visible
			#if (0)
			color2.a = 1 - color2.a;
			color2.r = 1 - color2.r;
			color2.g = 1 - color2.g;
			color2.b = 1 - color2.b;
			#endif

			index = WrappedIndex(x % TW, y, TP/TByteDepth, TH);
			TargetData = &((unsigned char*)target)[index*TByteDepth];
			SetColorIn(TargetData, TFormat, color2);
		}
	}

	return deTRUE;
}

deBoolean deBitmap::CopyRect(const IdeBitmap* &Source, deRect *SourceRect, long TargetX, long TargetY)
{
	// copy data from another bitmap into this one
	// the mapping of pixels is 1:1 and so we only need a coordinate for the target, not a rect

	// SourceRect can be NULL, Source cannot
	if (Source == NULL)
		DEBUGRETURN(deFALSE,"CopyRect: Parameter 'Source' was NULL");
	deBitmap* pSource = (deBitmap*)(Source);
	if (pSource->m_Format != m_Format)
		DEBUGRETURN(deFALSE,"CopyRect: Source and target formats do not match");

	// a default rect in case one wasn't specified
	deRect MyRect = {0,0,pSource->m_Width,pSource->m_Height};
	if (SourceRect == NULL)
		SourceRect = &MyRect;

	int y, width, height, left, top, byterun;
	BYTE *sPtr, *tPtr;

	// compute the size of the rect that will ACTUALLY get copied
	// since it might hang off the edge somewhere
	width = SourceRect->Right - SourceRect->Left;
	height = SourceRect->Bottom - SourceRect->Top;
	left = max(SourceRect->Left, 0);
	top = max(SourceRect->Top, 0);

	// uh, can't copy a negative size
	if (width < 0 || height < 0)
		DEBUGRETURN(deFALSE,"CopyRect: width or height of rect was negative");
	if (left >= pSource->m_Width || top >= pSource->m_Height)
		DEBUGRETURN(deTRUE,"CopyRect: specified rect was not in source bitmap area"); // does return true

	// now recompute a few sizes
	if (left + width > pSource->m_Width)
		width = pSource->m_Width - left;
	if (top + height > pSource->m_Height)
		height = pSource->m_Height - top;
	if (TargetX + width > m_Width)
		width = m_Width - TargetX;
	if (TargetY + height > m_Height)
		height = m_Height - TargetY;

	// figure how many bytes per row need copying
	byterun = width * m_ByteDepth;

	// last-chance bail out... this shouldn't really happen
	if (m_ByteDepth <= 1 || m_ByteDepth > 4)
		return deFALSE;

	// copy all the data
	for (y = 0; y < height; ++y)
	{
		sPtr = &(pSource->m_Data[0][m_ByteDepth*((y+top)*pSource->m_Width + left)]);
		tPtr = &(this->m_Data[0][m_ByteDepth*((y+TargetY)*this->m_Width + TargetX)]);
		memcpy(tPtr, sPtr, byterun);
	}
	
	// success
	return deTRUE;
}

IdeDriver::BPP deBitmap::GetFormat()
{	return m_Format; }

long deBitmap::GetWidth()
{	return m_Width;		}

long deBitmap::GetHeight()
{	return m_Height;	}

long deBitmap::GetBitDepth()
{	return m_ByteDepth*8;	}

long deBitmap::GetByteDepth()
{	return m_ByteDepth;	}

DWORD deBitmap::GetImageSize()
{	return m_ImageSize;	}

long deBitmap::GetNumFaces()
{	return m_Data.size(); }

deBoolean deBitmap::IsRenderTarget()
{	return m_RenderTarget; }

long deBitmap::GetScalingFactor()
{	return 1; }

long deBitmap::GetMipLevels()
{	return m_MipLevels; }

deBoolean deBitmap::SetData(void *data, long ByteLength, u32 FaceNum)
{
	u8**pMyData;
	if (FaceNum >= m_Data.size())
		DEBUGRETURN(deFALSE, "SetData: invalid face number");
	pMyData = &(m_Data[FaceNum]);

	delete[] *pMyData;
	if (ByteLength > 0)
	{
		*pMyData = new BYTE[ByteLength];
		memcpy(*pMyData, data, ByteLength);
	}
	else
	{
		*pMyData = (BYTE*)data;
	}
	return deTRUE;
}

void deBitmap::SetWidth(long width)
{	m_Width = width & 0x7FFFFFFF;	SetImageSize((m_ByteDepth*m_Width*m_Height));}

void deBitmap::SetHeight(long height)
{	m_Height = height & 0x7FFFFFFF;	SetImageSize((m_ByteDepth*m_Width*m_Height));}

void deBitmap::SetFormat(IdeDriver::BPP Format)
{	m_Format = Format; m_ByteDepth = BPPByteDepth(Format);  SetImageSize(m_ByteDepth*m_Width*m_Height);}

void deBitmap::SetBitDepth(long depth)
{	m_ByteDepth = (depth & 0x7FFFFFFF)/8;	SetImageSize((m_ByteDepth*m_Width*m_Height));}

void deBitmap::SetImageSize(long size)
{	m_ImageSize = size;	}

void deBitmap::SetRenderTarget(deBoolean RenderTarget)
{	m_RenderTarget = RenderTarget; }

void deBitmap::SetMipLevels(long MipLevels)
{	m_MipLevels = MipLevels; }

deBoolean deBitmap::SetNumFaces(long NumFaces)
{
	// flat texture or cubemap only
	if (NumFaces == 1 || NumFaces == 6)
	{
		int size;
		size = m_Data.size();
		if (NumFaces < size)
		{
			int k;
			for (k = NumFaces; k < size; ++k)
			{
				delete[] m_Data[k];
				m_Data[k] = NULL;
			}
		}
		m_Data.resize(NumFaces);
		return deTRUE;
	}
	return deFALSE;
}







deBitmapProxy::deBitmapProxy(IdeBitmap* bmp)
:	m_BMP(bmp),
	m_SavePixels(deFALSE)
{
	if (m_BMP)
		m_BMP->Claim();
	m_BitmapName[0] = '\0';
}

deBitmapProxy::~deBitmapProxy()
{
	if (m_BMP)
		m_BMP->Release();
}

void* deBitmapProxy::GetWOInterface(long interface_id)
{
	if (interface_id == s_BitmapProxyWOInterfaceID)
		return (IdeBitmapProxy*)(this);
	if (interface_id == 0)
		return (IdeWorldObject*)(this);
	return NULL;
}

void deBitmapProxy::SetBitmap(IdeBitmap* bmp)
{
	if (bmp)
	{
		bmp->Claim();
		bmp->GetFilenameBuffer(m_BitmapName, 255);
	}
	else
		m_BitmapName[0] = '\0';
	if (m_BMP)
		m_BMP->Release();
	m_BMP = bmp;
}

IdeBitmap* deBitmapProxy::GetBitmap()
{
	// if we have no bitmap, but we know the name of a bitmap, let's try to load it
	// this will happen if we've deserialized but not had the bitmap retrieved yet
	// effectively allowing deferred loading of bitmaps
	if (!m_BMP && m_BitmapName[0])
	{
		m_BitmapName[255] = '\0'; // make sure it's terminated
		IdeResourceManager* manager = IdeResourceManager_GetManager();
		IdeResourceBase* rsc = manager->GetResourceThreaded(m_BitmapName, NULL, deTRUE);
		if (rsc)
			m_BMP = (IdeBitmap*)rsc->GetRscInterface(deBitmap::s_BitmapRscInterfaceID);
	}
	return m_BMP;
}

void deBitmapProxy::SetFilename(const char* filename)
{
	long len;
	len = strlen(filename);
	len = min(255, len);
	memcpy(m_BitmapName, filename, len);
	m_BitmapName[len] = '\0';
}

void deBitmapProxy::SettingSavePixels(deBoolean Save)
{
	m_SavePixels = Save;
}

const char* deBitmapProxy::GetFilename()
{
	if (m_BMP)
		return m_BMP->GetFilename();
	else
		return NULL;
}

void deBitmapProxy::GetFilenameBuffer(char *buffer, long buffersize)
{
	if (m_BMP)
		m_BMP->GetFilenameBuffer(buffer, buffersize);
}

long deBitmapProxy::GetWidth()
{
	if (!m_BMP)
		return 0;
	return m_BMP->GetWidth();
}

long deBitmapProxy::GetHeight()
{
	if (!m_BMP)
		return 0;
	return m_BMP->GetHeight();
}

IdeDriver::BPP deBitmapProxy::GetFormat()
{
	if (!m_BMP)
		return IdeDriver::BPP_UNKNOWN;
	return m_BMP->GetFormat();
}

struct deBitmapProxy_Header_t
{
	short VersionMajor;
	short VersionMinor;
};
static const deBitmapProxy_Header_t deBitmapProxy_Header = {0,1};
/*
Version 0.1:
October 13, 2002

short		Version_Major
short		Version_Minor
ushort		NumChars
char		BitmapName[NumChars]
uchar		SavedPixels
{if (SavedPixels)}
	long	Width
	long	Height
	enum IdeDriver::BPP PixelFormat
	long	NumFaces
	{foreach face}
		byte	Pixels[Width*Height*datasize(PixelFormat)]
*/
deBoolean deBitmapProxy::Serialize(IdeFile* FS)
{
	DE3D_STREAM_PUT(FS, deBitmapProxy_Header.VersionMajor, u16);
	DE3D_STREAM_PUT(FS, deBitmapProxy_Header.VersionMinor, u16);

	u16 len;
	if (!m_BitmapName[0])
	{
		m_BMP->GetFilenameBuffer(m_BitmapName, 255);
	}
	len = (u16)strlen(m_BitmapName);
	
	DE3D_STREAM_PUT(FS, len, u16);
	DE3D_STREAM_PUTP(FS, m_BitmapName, len);

	deBoolean ReallySavePixels = m_SavePixels;
	if (!m_BMP)
		ReallySavePixels = deFALSE;
	DE3D_STREAM_PUT(FS, ReallySavePixels, u8);
	if (ReallySavePixels)
	{
		long i, width, height, numfaces, bytedepth;
		IdeDriver::BPP format;
		void* data;
		if (m_BMP)
		{
			width = m_BMP->GetWidth();
			height = m_BMP->GetHeight();
			format = m_BMP->GetFormat();
			bytedepth = BPPByteDepth(format);
			numfaces = m_BMP->GetNumFaces();
			data = m_BMP->GetData(0);
		}
		else
		{
			width = 0;
			height = 0;
			format = IdeDriver::BPP_UNKNOWN;
			bytedepth = 0;
			numfaces = 0;
		}
		DE3D_STREAM_PUT(FS, width, s32);
		DE3D_STREAM_PUT(FS, height, s32);
		DE3D_STREAM_PUT(FS, format, s32);
		DE3D_STREAM_PUT(FS, numfaces, s32);
		for (i = 0; i < numfaces; ++i)
		{
			data = m_BMP->GetData(i);
			DE3D_STREAM_PUTP(FS, data, width*height*bytedepth);
		}
	}

	return deTRUE;
}

deBoolean deBitmapProxy::DeSerialize(IdeFile* FS, long DataLength)
{
	if (FS == NULL)
		return deFALSE;
	
	u32 AmtRead = 0;
	u16 VersionMajor, VersionMinor;
	
	DE3D_STREAM_GET(FS, VersionMajor, u16, AmtRead, DataLength);
	DE3D_STREAM_GET(FS, VersionMinor, u16, AmtRead, DataLength);
	if (VersionMajor > deBitmapProxy_Header.VersionMajor ||
		(VersionMajor == deBitmapProxy_Header.VersionMajor && VersionMinor > deBitmapProxy_Header.VersionMinor))
		return deFALSE;
	
	if (VersionMajor == 0 && VersionMinor == 1)
	{
		// read the bitmap filename
		{
			u16 len;
			DE3D_STREAM_GET(FS, len, u16, AmtRead, DataLength);
			DE3D_STREAM_GETP(FS, m_BitmapName, len, AmtRead, DataLength);
			m_BitmapName[len] = '\0';
		}
		DE3D_STREAM_GET(FS, m_SavePixels, s8, AmtRead, DataLength);
		if (m_SavePixels)
		{
			// we have saved pixels, read them out and into the bitmap
			long i, width, height, numfaces, imagesize;
			IdeDriver::BPP format;
			DE3D_STREAM_GET(FS, width, s32, AmtRead, DataLength);
			DE3D_STREAM_GET(FS, height, s32, AmtRead, DataLength);
			DE3D_STREAM_GET(FS, format, s32, AmtRead, DataLength);
			DE3D_STREAM_GET(FS, numfaces, s32, AmtRead, DataLength);
			if (numfaces != 1 && numfaces != 6)
				return deFALSE;
			if (BPPByteDepth(format) <= 0)
				return deFALSE;
			// create a bitmap with the name, implicitly claim it
			m_BMP = new deBitmap(m_BitmapName);
			// apply the various attributes
			m_BMP->SetWidth(width);
			m_BMP->SetHeight(height);
			m_BMP->SetFormat(format);
			m_BMP->SetNumFaces(numfaces);

			imagesize = m_BMP->GetImageSize();

			void* data;
			for (i = 0; i < numfaces; ++i)
			{
				data = new BYTE[imagesize];
				DE3D_STREAM_GETP(FS, data, imagesize, AmtRead, DataLength);
				m_BMP->SetData(data, i);
			}
		}
	}
	return deTRUE;
}


