///////////////////////////////////////////////////////////////////////////////
/// @file deFSPlugin.cpp
///
/// @brief plugin file system interface
///
/// @author Lightning
///
/// 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 May 2003
/// @author Lightning
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#include "deFile_priv.hpp"
#include "deByteOrder.hpp"

//definition for 1 megabyte
#define deFilePlugin_1MB	(1024*1024)

//a define for the ID of a virtual fs file. Done here as not user needed
#ifdef DE3D_CPU_LITTLE_ENDIAN
#define PluginFileID		0x44504C46		//'DPLF', Destiny3D PLugin File
#define PluginFileVersion	0x00000001		//version of the PLF, current is 0.0.0.1
#else
#define PluginFileID		0x464C5044		//'DPLF', Destiny3D PLugin File
#define PluginFileVersion	0x01000000		//version of the PLF, current is 0.0.0.1
#endif

deFilePlugin::deFilePlugin()
{
	//set everything to the defaults
	priv_BaseFile = NULL;
	priv_BaseFS = NULL;
	priv_OwnBaseFile = deFALSE;
	priv_CurrentPosition = 0;
	priv_OpenFlags = 0;
	priv_FileData = NULL;
	priv_FileDataCount = 0;
	priv_LastBlockSize = 0;
	priv_PluginList_First = NULL;
	priv_PluginList_Last = NULL;
	priv_PluginLocked = deFALSE;

	priv_FileBlock = 0;
	priv_FileBuffer = NULL;
}

deFilePlugin::~deFilePlugin()
{
	//close any file that may exist
	this->Close();

	//clear the plugins out
	ReleaseAllPlugins();
}

void* deFilePlugin::GetInterface(IdeFile::interface_t i)
{
	if (i == IdeFile::iface_IdeFile)
		return (IdeFile*)(this);

	if (i == IdeFile::iface_IdeFilePlugin)
		return (IdeFilePlugin*)(this);

	return NULL;
}

int deFilePlugin::Release()
{
	delete this;
	return 0;
}

deBoolean deFilePlugin::Open(IdeFileSystem *BaseFS, char *Filename, long OpenFlags)
{
	IdeFile *	NewFile;
	deFSReal *	FSReal;

	//if no basefs, create a realfs
	if(!BaseFS)
	{
		FSReal = new deFSReal;
		if(!FSReal)
			return deFALSE;

		//store the basefs so we cn close it
		priv_BaseFS = FSReal;
		BaseFS = FSReal;
	}

	//open up a file to use as a plugin file, make sure we can read from the file
	NewFile = BaseFS->Open(Filename, (OpenFlags | deFile_Open_Read));
	if(!NewFile)
		return deFALSE;

	//get everything setup
	if(!Open(NewFile))
	{
		NewFile->Release();
		return deFALSE;
	}

	//all setup, say we own the base file
	priv_OwnBaseFile = deTRUE;
	return deTRUE;
}

deBoolean deFilePlugin::Open(IdeFile *BaseFile)
{
	PluginHeader		HeaderInfo;
	long				FileDataSize;
	long				Counter;

	//open up a file as a plugin file

	//get the file flags
	priv_OpenFlags = BaseFile->GetOpenFlags();

	//first, make sure we are at the beginning of the file
	BaseFile->SetPosition(0, deFile_Seek_Start);

	//if we have any size for the file then read the data about it
	if(BaseFile->GetSize() != 0)
	{
		//read the header now
		if(BaseFile->Read(&HeaderInfo, sizeof(PluginHeader)) != sizeof(PluginHeader))
			return deFALSE;

		//make sure the byte order is proper
		HeaderInfo.ID = HeaderInfo.ID;
		HeaderInfo.Version = HeaderInfo.Version;
		HeaderInfo.DataOffset = deByteOrderBE32(HeaderInfo.DataOffset);
		HeaderInfo.LastBlockSize = deByteOrderBE32(HeaderInfo.LastBlockSize);
		HeaderInfo.EntryCount = deByteOrderBE16(HeaderInfo.EntryCount);

		//we got the header, check it
		if(HeaderInfo.ID != PluginFileID || HeaderInfo.Version != PluginFileVersion)
			return deFALSE;

		//move to the file data area
		if(BaseFile->SetPosition(HeaderInfo.DataOffset, deFile_Seek_Start) != HeaderInfo.DataOffset)
			return deFALSE;

		//set the number of entries in the array
		priv_FileDataCount = HeaderInfo.EntryCount;

		//allocate memory for the entries
		FileDataSize = priv_FileDataCount * sizeof(PluginFileData);

		//verify the size is correct, we should hit the end of the file if we read the data in from
		//the offset of the data in the file
		if(BaseFile->GetSize() != FileDataSize + HeaderInfo.DataOffset)
			return deFALSE;

		//set the last block's size
		priv_LastBlockSize = HeaderInfo.LastBlockSize;

		//size is valid
		if(priv_FileDataCount)
		{
			//got entries to read, read them
			priv_FileData = (PluginFileData *)malloc(FileDataSize);
			if(!priv_FileData)
				return deFALSE;

			//read the data in
			if(BaseFile->Read(priv_FileData, FileDataSize) != FileDataSize)
			{
				free(priv_FileData);
				priv_FileData = NULL;
				return deFALSE;
			}

			//make sure all data is proper
			for(Counter=0; Counter < priv_FileDataCount; Counter++)
			{
				priv_FileData[Counter].RealFilePos = deByteOrderBE32(priv_FileData[Counter].RealFilePos);
				priv_FileData[Counter].Size = deByteOrderBE32(priv_FileData[Counter].Size);
			}
		}
		else
		{
			priv_FileData = NULL;		//make 1 file entry to work with
			priv_FileDataCount = 0;		//1 block to work with
			priv_LastBlockSize = 0;		//no size to the last block
		}
	}
	else
	{
		//must be a new file, setup the header and data array
		memset(&HeaderInfo, 0, sizeof(HeaderInfo));
		HeaderInfo.ID = PluginFileID;
		HeaderInfo.Version = PluginFileVersion;

		//write it out
		if(BaseFile->Write(&HeaderInfo, sizeof(PluginHeader)) != sizeof(PluginHeader))
			return deFALSE;

		//we got the header setup in the file, now setup the array entries
		priv_FileData = NULL;
		priv_FileDataCount = 0;
		priv_LastBlockSize = 0;
	}

	//finalize some flags
	priv_BaseFile = BaseFile;			//set the base file entry
	priv_OwnBaseFile = deFALSE;			//we dont own the base file
	priv_CurrentPosition = 0;			//make sure we are at the start of the file
	priv_PluginLocked = deTRUE;			//lock the plugins so they cant be updated
	priv_FileBlock = 0;
	priv_FileBuffer = NULL;
	
	//If we have no block to work with initially, set one up
	if(!priv_FileData)
	{
		//grab a 1MB chunk of memory for the file blocks that we store
		priv_FileBuffer = malloc(deFilePlugin_1MB);
		if(!priv_FileBuffer)
			return deFALSE;

		priv_FileData = (PluginFileData *)malloc(sizeof(PluginFileData));
		memset(priv_FileData, 0, sizeof(PluginFileData));

		priv_FileDataCount = 1;
	}
	else
	{
		//read the first block in
		priv_FileBlock = -1;
		GetFileBlock(0);
	}

	//everything is done, finish
	return deTRUE;
}

deBoolean deFilePlugin::Close()
{
	long				DataPos;
	long				FileDataSize;
	PluginHeader		FileHeader;
	long				Counter;

	//close an opened file

	//if no current file, fail
	if(!priv_BaseFile)
		return deFALSE;

	//if we can write to the file then goto the end and write out our tree info
	if(priv_OpenFlags & deFile_Open_Write)
	{
		//if the last block has been modified then write it out
		if(priv_FileBlockChanged)
			WriteCurrentFileBlock();

		//defrag the file
		DefragFile();

		//figure out the size of the data
		FileDataSize = priv_FileDataCount * sizeof(PluginFileData);

		//we can write to it, figure out the position
		if(priv_FileDataCount)
		{
			//set the counter to the last block
			Counter = priv_FileDataCount - 1;

			//find the last physical block in the file that actually has data
			//the file may be extra large but contain empty blocks
			DataPos = 0;
			for(Counter=0; Counter < priv_FileDataCount; Counter++)
			{
				//if the file position is larger, then get the block
				if(priv_FileData[Counter].RealFilePos > priv_FileData[DataPos].RealFilePos)
					DataPos = Counter;
			}

			//if we have an entry, get the position
			if(priv_FileData[DataPos].RealFilePos != 0)
			{
				//get the data position
				DataPos = priv_FileData[DataPos].RealFilePos + priv_FileData[DataPos].Size;

				//set the position and write the data out
				priv_BaseFile->SetPosition(DataPos, deFile_Seek_Start);

				//make sure all data is proper
				for(Counter=0; Counter < priv_FileDataCount; Counter++)
				{
					priv_FileData[Counter].RealFilePos = deByteOrderBE32(priv_FileData[Counter].RealFilePos);
					priv_FileData[Counter].Size = deByteOrderBE32(priv_FileData[Counter].Size);
				}

				//write out the blocks
				priv_BaseFile->Write(priv_FileData, FileDataSize);
			}
			else
				DataPos = sizeof(PluginHeader);		//invalid counter, no blocks, set datapos to after the header

		}
		else
			DataPos = sizeof(FileHeader);	//no entries at all, place it after the header

		//update the header
		FileHeader.ID = PluginFileID;
		FileHeader.Version = PluginFileVersion;
		FileHeader.EntryCount = deByteOrderBE16(priv_FileDataCount);
		FileHeader.DataOffset = deByteOrderBE32(DataPos);
		FileHeader.LastBlockSize = deByteOrderBE32(priv_LastBlockSize);
		
		//move to the beginning and write the header
		priv_BaseFile->SetPosition(0, deFile_Seek_Start);

		//write it
		priv_BaseFile->Write(&FileHeader, sizeof(PluginHeader));

		//resize the file to the correct size incase things were removed
		priv_BaseFile->SetSize(FileDataSize + DataPos);
	}

	//release the buffer
	if(priv_FileBuffer)
		free(priv_FileBuffer);
	priv_FileBuffer = NULL;

	//if we own the base file, release it
	if(priv_OwnBaseFile)
	{
		priv_BaseFile->Close();
		priv_BaseFile->Release();
	}

	//if there is a basefs, release it as we own it
	if(priv_BaseFS)
	{
		priv_BaseFS->Release();
		priv_BaseFS = NULL;
	}
	
	//reset any data about a file
	priv_BaseFile = NULL;
	priv_OwnBaseFile = deFALSE;
	priv_CurrentPosition = 0;
	priv_OpenFlags = 0;

	//free the file data
	free(priv_FileData);
	priv_FileData = NULL;
	priv_FileDataCount = 0;
	priv_LastBlockSize = 0;
	
	//unlock the plugins
	priv_PluginLocked = deFALSE;

	//all done
	return deTRUE;
}

deBoolean deFilePlugin::GetFileBlock(long ReadBlock)
{
	void *	TempBuffer;
	long	EndLength;
	long	DecodeLength;
	
	//if we are reading the block we have, just exit
	if(ReadBlock == priv_FileBlock)
		return true;

	//erase the block we have and read in a new block of data from the file
	//we also have to run it thru the various plugins we have
	if(priv_FileBuffer)
		free(priv_FileBuffer);
	priv_FileBuffer = NULL;

	//incase something fails, set the block to an invalid block
	priv_FileBlock = -1;

	//set that the block hasnt been changed
	priv_FileBlockChanged = deFALSE;

	//if the read block is too large, fail
	if(ReadBlock >= priv_FileDataCount)
		return deFALSE;		//block too large, fail

	//if the real file position is 0, then it is a signal of an empty block
	if(priv_FileData[ReadBlock].RealFilePos == 0)
	{
		//an empty block, handle this special by returning an empty buffer
		priv_FileBuffer = malloc(deFilePlugin_1MB);
		if(!priv_FileBuffer)
			return deFALSE;
		memset(priv_FileBuffer, 0, deFilePlugin_1MB);
	}
	else
	{
		//set the file position of the base file
		EndLength = priv_BaseFile->SetPosition(priv_FileData[ReadBlock].RealFilePos, deFile_Seek_Start);
		if(EndLength != priv_FileData[ReadBlock].RealFilePos)
			return deFALSE;		//invalid position, fail

		//allocate the buffer
		TempBuffer = malloc(priv_FileData[ReadBlock].Size);
		if(!TempBuffer)
			return deFALSE;		//cant get the buffer

		//got a buffer, read the file for it
		EndLength = priv_BaseFile->Read(TempBuffer, priv_FileData[ReadBlock].Size);
		if(EndLength != priv_FileData[ReadBlock].Size)
		{
			//didnt read in the whole block, fail
			free(TempBuffer);
			return deFALSE;
		}

		//we got the buffer from the file, decode it
		DecodeLength = priv_FileData[ReadBlock].Size;
		priv_FileBuffer = ApplyPluginDecode(TempBuffer, &DecodeLength);
		free(TempBuffer);

		//if no buffer returned, fail
		if(!priv_FileBuffer)
			return deFALSE;

		//figure out the resulting buffer length, if we are the end entry
		//then we need to check the header for the proper size of the resulting buffer
		if(ReadBlock == priv_FileDataCount - 1)
			EndLength = priv_LastBlockSize;
		else
			EndLength = deFilePlugin_1MB;

		//verify that the length decoded matches what we expected
		if(DecodeLength != EndLength)
		{
			//something is wrong, free the buffer and fail
			free(priv_FileBuffer);
			return deFALSE;
		}

		//if we are not 1MB in size, then adjust the buffer
		if(DecodeLength != deFilePlugin_1MB)
		{
			//allocate the final buffer
			TempBuffer = malloc(deFilePlugin_1MB);
			if(!TempBuffer)
				return deFALSE;		//cant get the buffer
		
			//copy the original data to the new 1MB buffer (incase we write data)
			memcpy(TempBuffer, priv_FileBuffer, DecodeLength);

			//erase the rest of the array
			memset(&((char *)TempBuffer)[DecodeLength], 0, (deFilePlugin_1MB - DecodeLength));

			//swap the buffers
			free(priv_FileBuffer);
			priv_FileBuffer = TempBuffer;
			TempBuffer = NULL;
		}
	}

	//everything seems fine, set the block we read
	priv_FileBlock = ReadBlock;
	return deTRUE;
}

deBoolean deFilePlugin::WriteCurrentFileBlock()
{
	void *	WriteData;
	long	WriteLength;
	long	SetPosition;

	//write out the current block in memory to the file

	//figure out the proper length of the buffer.
	if(priv_FileBlock == priv_FileDataCount - 1)
		WriteLength = priv_LastBlockSize;		//last block, go with it's size
	else
		WriteLength = deFilePlugin_1MB;

	//first, encode the data thru the plugins
	WriteData = ApplyPluginEncode(priv_FileBuffer, &WriteLength);
	if(!WriteData || !WriteLength)
		return deFALSE;

	//if the size is too large and not the last block, then put at the end of the file for now, we will resize
	//on close
	if(WriteLength > priv_FileData[priv_FileBlock].Size)
	{
		//size is too large, move to the end of the file
		SetPosition = priv_BaseFile->SetPosition(0, deFile_Seek_End);
		if(SetPosition != priv_BaseFile->GetSize())
		{
			free(WriteData);
			return deFALSE;
		}
	}
	else
	{
		//size is fine, move the position of the file to the beginning of the block
		SetPosition = priv_BaseFile->SetPosition(priv_FileData[priv_FileBlock].RealFilePos, deFile_Seek_Start);
		if(SetPosition != priv_FileData[priv_FileBlock].RealFilePos)
		{
			free(WriteData);
			return deFALSE;
		}
	}

	//write out the block
	if(priv_BaseFile->Write(WriteData, WriteLength) != WriteLength)
		return deFALSE;

	//free our block we wrote
	free(WriteData);

	//everything is fine, update the block
	priv_FileData[priv_FileBlock].RealFilePos = SetPosition;
	priv_FileData[priv_FileBlock].Size = WriteLength;

	//set that it has been written
	priv_FileBlockChanged = deFALSE;
	
	//return true
	return deTRUE;
}

long deFilePlugin::Read(void *Buffer, long Length)
{
	long	ReadBlock;
	long	ReadLength;
	long	LengthToRead;
	long	PositionToRead;
	
	//if no base file, fail
	if(!priv_BaseFile)
		return deFALSE;

	//if we cant read, fail
	if(!(priv_OpenFlags & deFile_Open_Read))
		return deFALSE;

	//go thru and read the data from the file
	ReadLength = 0;
	do
	{
		//calculate the read block we need from the current position
		//in the file
		ReadBlock = priv_CurrentPosition / deFilePlugin_1MB;

		//see if we have the right block in memory
		if(ReadBlock != priv_FileBlock)
		{
			//if a block has changed, write it out before reading in another
			if(priv_FileBlockChanged)
			{
				//write the block out, if it fails return
				if(!WriteCurrentFileBlock())
					return ReadLength;
			}

			//not the right block, read the right block into memory
			if(!GetFileBlock(ReadBlock))
				return ReadLength;
		}

		//we have the right block, find out the position to read
		PositionToRead = priv_CurrentPosition % deFilePlugin_1MB;

		//figure out how much of the buffer to read
		if(Length > (deFilePlugin_1MB - PositionToRead))
		{
			//length goes over the boundary, read up to the boundary
			LengthToRead = (deFilePlugin_1MB - PositionToRead);
		}
		else if((ReadBlock == priv_FileDataCount) && (Length > priv_LastBlockSize))
		{
			//on the last block, and the length is larger than the block size
			//keep it from going over the edge
			LengthToRead = priv_LastBlockSize;

			//nothing left to read, adjust the length so it will be 0
			Length = priv_LastBlockSize;
		}
		else
			LengthToRead = Length;		//read out what they want

		//adjust the length of data for what we will read incase
		//we are reading multiple blocks
		Length = Length - LengthToRead;

		//we have position, we have the buffer, we have the length, read it
		memcpy(&((char *)Buffer)[ReadLength], &((char *)priv_FileBuffer)[PositionToRead], LengthToRead);

		//adjust the size of the data we have read
		ReadLength = ReadLength + LengthToRead;

		//change the position in the file
		priv_CurrentPosition = priv_CurrentPosition + LengthToRead;

	} while(Length);	//keep looping until we read it all

	//return the length of the read
	return ReadLength;
}

long deFilePlugin::Write(void *Buffer, long Length)
{
	long	WriteBlock;
	long	WriteLength;
	long	LengthToWrite;
	long	PositionToWrite;

	//if no base file, fail
	if(!priv_BaseFile)
		return deFALSE;

	//if we cant write, fail
	if(!(priv_OpenFlags & deFile_Open_Write))
		return deFALSE;

	//adjust the size of the file to allow for the data
	if((priv_CurrentPosition+Length) > GetSize())
		SetSize(priv_CurrentPosition + Length);

	//go thru and write the data to the file
	WriteLength = 0;
	do
	{
		//calculate the write block we need from the current position
		//in the file
		WriteBlock = priv_CurrentPosition / deFilePlugin_1MB;

		//see if we have the right block in memory
		if(WriteBlock != priv_FileBlock)
		{
			//if a block has changed, write it out before reading in another
			if(priv_FileBlockChanged)
			{
				//write the block out, if it fails return
				if(!WriteCurrentFileBlock())
					return WriteLength;
			}

			//not the right block, read the right block into memory
			if(!GetFileBlock(WriteBlock))
				return WriteLength;
		}

		//we have the right block, find out the position to write
		PositionToWrite = priv_CurrentPosition % deFilePlugin_1MB;

		//figure out how much of the buffer to read
		if(Length > (deFilePlugin_1MB - PositionToWrite))
		{
			//length goes over the boundary, write up to the boundary
			LengthToWrite = (deFilePlugin_1MB - PositionToWrite);
		}
		else
			LengthToWrite = Length;		//write out what they want

		//adjust the length of data for what we will write incase
		//we are writing multiple blocks
		Length = Length - LengthToWrite;

		//we have position, we have the buffer, we have the length, write it
		memcpy(&((char *)priv_FileBuffer)[PositionToWrite], &((char *)Buffer)[WriteLength], LengthToWrite);

		//say we modified the block
		priv_FileBlockChanged = deTRUE;

		//adjust the size of the data we have written
		WriteLength = WriteLength + LengthToWrite;

		//change the position in the file
		priv_CurrentPosition = priv_CurrentPosition + LengthToWrite;
	} while(Length);	//keep looping until we write it all

	//return the length of the write
	return WriteLength;
}

long deFilePlugin::GetPosition()
{
	//return the current position in a file

	//if no file, return -1 for failure
	if(!priv_BaseFile)
		return -1;

	//return the current position
	return priv_CurrentPosition;
}

long deFilePlugin::SetPosition(long NewPosition, Seek SeekType)
{
	long	FileSize;

	//set the new position in the file

	//if no file, fail
	if(!priv_BaseFile)
		return -1;

	//set the position based on the seek type
	switch(SeekType)
	{
		case deFile_Seek_Start:
			//set CurrentPosition to the new position

			//make sure NewPosition is not invalid
			if(NewPosition < 0)
				return -1;

			//set it
			priv_CurrentPosition = NewPosition;
			break;

		case deFile_Seek_Current:
			//add to the current position the new position

			//make sure it isn't invalid
			if((priv_CurrentPosition + NewPosition) < 0)
				return -1;

			//set it
			priv_CurrentPosition = priv_CurrentPosition + NewPosition;
			break;

		case deFile_Seek_End:
			//take the length of the file and subtract the new position from it

			//calculate the file size
			FileSize = (priv_FileDataCount - 1) * deFilePlugin_1MB;
			FileSize = FileSize + priv_LastBlockSize;

			//make sure it isn't invalid
			if((FileSize - NewPosition) < 0)
				return -1;

			//set it
			priv_CurrentPosition = FileSize - NewPosition;
			break;

		default:
			//invalid seek
			return -1;
	};

	//return the new position
	return priv_CurrentPosition;
}

long deFilePlugin::GetSize()
{
	long	FileSize;

	//if no base file, fail
	if(!priv_BaseFile)
		return 0;

	//calculate the size of all the blocks but the last one
	FileSize = (priv_FileDataCount-1) * deFilePlugin_1MB;

	//add in the size of the last block
	FileSize = FileSize + priv_LastBlockSize;

	//return the size
	return FileSize;
}

deBoolean deFilePlugin::SetSize(long NewSize)
{
	PluginFileData *	NewFileData;
	short				NewDataCount;
	
	//if no base file, fail
	if(!priv_BaseFile)
		return deFALSE;

	//if we cant write, fail
	if(!(priv_OpenFlags & deFile_Open_Write))
		return deFALSE;

	//set the size

	//figure out block count, add a block if we have extra data
	//1 block per mb = 65gb, no need for a long
	NewDataCount = (short)((NewSize / deFilePlugin_1MB) + ((NewSize % deFilePlugin_1MB) != 0));

	//got the new block count, see if we need to grow or shrink
	if(NewDataCount > priv_FileDataCount)
	{
		//add extra blocks
		NewFileData = (PluginFileData *)malloc(NewDataCount * sizeof(PluginFileData));
		if(!NewFileData)
			return deFALSE;

		//copy over the whole original array
		memcpy(NewFileData, priv_FileData, priv_FileDataCount * sizeof(PluginFileData));

		//erase the rest of the array
		//a position of 0 indicates an empty block. of course an empty block has 0 for size
		memset(&NewFileData[priv_FileDataCount], 0, (NewDataCount - priv_FileDataCount) * sizeof(PluginFileData));

		//erase the original array and replace it
		free(priv_FileData);
		priv_FileData = NewFileData;

		//swap the count
		priv_FileDataCount = NewDataCount;
	}
	else if(NewDataCount < priv_FileDataCount)
	{
		//shrink the number of blocks
		NewFileData = (PluginFileData *)malloc(NewDataCount * sizeof(PluginFileData));
		if(!NewFileData)
			return deFALSE;

		//copy over enough entries
		memcpy(NewFileData, priv_FileData, NewDataCount * sizeof(PluginFileData));

		//if the new size is not on a 1MB boundary then read in the last block
		//to resize it
		if(NewSize % deFilePlugin_1MB)
		{
			//need to adjust the last block

			//if the current block has been modified, write it out if it is still
			//a valid area
			if(priv_FileBlockChanged && (priv_FileBlock < (NewDataCount - 1)))
			{
				if(!WriteCurrentFileBlock())
					return deFALSE;
			}

			//read in the block
			if(!GetFileBlock(NewDataCount - 1))
				return deFALSE;

			//swap the count and array
			free(priv_FileData);
			priv_FileData = NewFileData;
			priv_FileDataCount = NewDataCount;
		}

		//erase the rest of the block that is not valid data
		memset(&((char *)priv_FileBuffer)[priv_LastBlockSize], 0, deFilePlugin_1MB - priv_LastBlockSize);
	}
	else
	{
		//the block count is the same, adjust the last block

		//if the new size is not on a 1MB boundary then read in the last block
		//to resize it

		//need to adjust the last block

		//if the current block has been modified, write it out
		//if we are not the last block
		if(priv_FileBlockChanged && (priv_FileBlock != (priv_FileDataCount - 1)))
		{
			if(!WriteCurrentFileBlock())
				return deFALSE;
		}

		//read in the block
		if(!GetFileBlock(priv_FileDataCount - 1))
			return deFALSE;
	}

	//set the last block size, we do not set to 0 as it is the size the end block should be
	//Have to allow 1MB sizes, but can not mod by 1MB+1 as that will affect >1MB sizes slightly
	//So remove 1 from the size, mod, add the 1 back if we even have a size to deal with
	if(NewSize)
		priv_LastBlockSize = ((NewSize - 1) % deFilePlugin_1MB) + 1;
	else
		priv_LastBlockSize = 0;
	
	//if our position is outside the file, adjust it
	if(priv_CurrentPosition > NewSize)
		priv_CurrentPosition = NewSize;

	return deTRUE;
}

s64 deFilePlugin::GetTime()
{
	//if no base file, fail
	if(!priv_BaseFile)
		return 0;

	//get the time of a file
	return priv_BaseFile->GetTime();
}

deBoolean deFilePlugin::SetTime(s64 Time)
{
	//if no base file, fail
	if(!priv_BaseFile)
		return deFALSE;

	//if we cant write, fail
	if(!(priv_OpenFlags & deFile_Open_Write))
		return deFALSE;

	//set the time of the file
	return priv_BaseFile->SetTime(Time);
}

deBoolean deFilePlugin::GetProperties(deFileProperties *Properties)
{
	//if no base file, fail
	if(!priv_BaseFile)
		return deFALSE;

	//get the properties
	if(!priv_BaseFile->GetProperties(Properties))
		return deFALSE;		//getting properties failed

	//insert the proper size of the file
	Properties->Size = GetSize();

	return deFALSE;
}

long deFilePlugin::GetOpenFlags()
{
	//if no base file, no flags
	if(!priv_BaseFile)
		return 0;

	//return the flags
	return priv_OpenFlags;
}

deBoolean deFilePlugin::AddPlugin(IdePlugin *Plugin)
{
	PluginList *	NewEntry;

	//add a plugin to the list

	//if the plugin list is locked, fail
	if(priv_PluginLocked)
		return deFALSE;

	//allocate a new entry
	NewEntry = (PluginList *)malloc(sizeof(PluginList));
	if(!NewEntry)
		return deFALSE;

	//set the new entry's plugin
	NewEntry->Plugin = Plugin;
	NewEntry->Next = NULL;
	NewEntry->Previous = priv_PluginList_Last;	//the entry before us was the last entry

	//tell the last entry of it
	if(priv_PluginList_Last)
		priv_PluginList_Last->Next = NewEntry;

	//update the last entry
	priv_PluginList_Last = NewEntry;

	//if no first entry exists, set it
	if(!priv_PluginList_First)
		priv_PluginList_First = NewEntry;
	
	return deTRUE;
}

deBoolean deFilePlugin::ReleasePlugin(IdePlugin *Plugin)
{
	PluginList *	ListEntry;

	//remove plugins from the list

	//if the list is locked, fail
	if(priv_PluginLocked)
		return deFALSE;

	//find the plugin
	ListEntry = priv_PluginList_First;
	while(ListEntry && ListEntry->Plugin != Plugin)
		ListEntry = ListEntry->Next;

	//if we didn't find it, fail
	if(!ListEntry)
		return deFALSE;

	//remove it from the previous entry
	if(ListEntry->Previous)
		ListEntry->Previous->Next = ListEntry->Next;		//set the entry before it to point after it
	else
		priv_PluginList_First = ListEntry->Next;			//first entry, change first to be next entry

	//remove it from the following entry
	if(ListEntry->Next)
		ListEntry->Next->Previous = ListEntry->Previous;	//set the entry after it to point before it
	else
		priv_PluginList_Last = ListEntry->Previous;			//last entry, change the last entry to the previous

	//free it
	free(ListEntry);
	return deTRUE;
}

deBoolean deFilePlugin::ReleaseAllPlugins()
{
	PluginList *	ListEntry;

	//remove all plugins from the list

	//if the list is locked, fail
	if(priv_PluginLocked)
		return deFALSE;

	//free the plugin list
	while(priv_PluginList_First)
	{
		ListEntry = priv_PluginList_First;
		priv_PluginList_First = priv_PluginList_First->Next;

		free(ListEntry);
	}

	//change the last entry
	priv_PluginList_Last = NULL;

	return deTRUE;
}

void *deFilePlugin::ApplyPluginEncode(void *Buffer, long *Length)
{
	PluginList *	Plugins;
	void *			NewBuffer;
	void *			NewBuffer2;
	DWORD			NewLength;
	DWORD			NewLength2;

	//apply the encode plugin function of all plugins to the buffer

	//if no plugins, return the buffer
	if(!priv_PluginList_First)
	{
		//we have to make a copy of the buffer, otherwise we may
		//end up freeing what is thought to be a temp buffer
		NewBuffer = malloc(*Length);
		if(!NewBuffer)
			return NULL;

		memcpy(NewBuffer, Buffer, *Length);
		return NewBuffer;
	}

	//go from the beginning to end of the chain
	Plugins = priv_PluginList_First;

	//first, run thru 1 loop outside to use Buffer
	//I could just make a copy of the data into NewBuffer and set NewLength
	//to the size but why not use a plugin while creating the buffer
	NewLength = Plugins->Plugin->MaxEncodeLength(*Length);
	if(!NewLength)
		return NULL;

	//allocate memory for a buffer
	NewBuffer = malloc(NewLength);
	if(!NewBuffer)
		return NULL;

	//encode it
	if(!Plugins->Plugin->EncodeData(Buffer, *Length, NewBuffer, &NewLength))
	{
		//something failed
		free(NewBuffer);
		return NULL;
	}

	//go to the next plugin entry
	Plugins = Plugins->Next;

	//loop thru whatever is left, above code is so extra if statements are removed
	while(Plugins)
	{
		//grab the length needed for an encode
		NewLength2 = Plugins->Plugin->MaxEncodeLength(NewLength);

		//if no length, fail
		if(!NewLength2)
		{
			free(NewBuffer);
			return NULL;
		}

		//allocate memory
		NewBuffer2 = malloc(NewLength2);
		if(!NewBuffer2)
		{
			//something failed
			free(NewBuffer);
			free(NewBuffer2);
			return NULL;
		}

		//encode it
		if(!Plugins->Plugin->EncodeData(NewBuffer, NewLength, NewBuffer2, &NewLength2))
		{
			//something failed
			free(NewBuffer);
			free(NewBuffer2);
			return NULL;
		}

		//free the original
		free(NewBuffer);

		//swap the buffer and length
		NewBuffer = NewBuffer2;
		NewLength = NewLength2;

		//go to next plugin
		Plugins = Plugins->Next;
	}

	//return the length of the new buffer
	*Length = NewLength;

	//return the buffer
	return NewBuffer;
}

void *deFilePlugin::ApplyPluginDecode(void *Buffer, long *Length)
{
	PluginList *	Plugins;
	void *			NewBuffer;
	void *			NewBuffer2;
	DWORD			NewLength;
	DWORD			NewLength2;

	//apply the decode plugin function of all plugins to the buffer

	//if no plugins, return the buffer
	if(!priv_PluginList_Last)
	{
		//we have to make a copy of the buffer, otherwise we may
		//end up freeing what is thought to be a temp buffer
		NewBuffer = malloc(*Length);
		if(!NewBuffer)
			return NULL;

		memcpy(NewBuffer, Buffer, *Length);
		return NewBuffer;
	}
	
	//decode goes in reverse order of encode
	Plugins = priv_PluginList_Last;

	//first, run thru 1 loop outside to use Buffer
	//I could just make a copy of the data into NewBuffer and set NewLength
	//to the size but why not use a plugin while creating the buffer
	NewLength = Plugins->Plugin->MaxDecodeLength(Buffer);
	if(!NewLength)
		return NULL;

	//allocate memory for a buffer
	NewBuffer = malloc(NewLength);
	if(!NewBuffer)
		return NULL;

	//decode it
	if(!Plugins->Plugin->DecodeData(Buffer, *Length, NewBuffer, &NewLength))
	{
		//something failed
		free(NewBuffer);
		return NULL;
	}

	//go to the previous plugin entry
	Plugins = Plugins->Previous;

	//loop thru whatever is left, above code is so extra if statements are removed
	while(Plugins)
	{
		//grab the length needed for a decode
		NewLength2 = Plugins->Plugin->MaxDecodeLength(NewBuffer);

		//if no length, fail
		if(!NewLength2)
		{
			free(NewBuffer);
			return NULL;
		}

		//allocate memory
		NewBuffer2 = malloc(NewLength2);
		if(!NewBuffer2)
		{
			//something failed
			free(NewBuffer);
			free(NewBuffer2);
			return NULL;
		}

		//decode it
		if(!Plugins->Plugin->DecodeData(NewBuffer, NewLength, NewBuffer2, &NewLength2))
		{
			//something failed
			free(NewBuffer);
			free(NewBuffer2);
			return NULL;
		}

		//free the original
		free(NewBuffer);

		//swap the buffer and length
		NewBuffer = NewBuffer2;
		NewLength = NewLength2;

		//go to previous plugin
		Plugins = Plugins->Previous;
	}

	//return the length of the new buffer
	*Length = NewLength;

	//return the buffer
	return NewBuffer;
}

deBoolean deFilePlugin::DefragFile()
{
	PluginFileData **	PluginEntries;
	PluginFileData *	TempPluginEntry;
	PluginFileData		FirstEntry;
	long				Counter;
	long				Counter2;
	long				NeededPos;
	void *				TempBuffer;
	long				BufferSize;

	//if no base file, fail
	if(!priv_BaseFile)
		return deFALSE;

	//setup the first entry
	memset(&FirstEntry, 0, sizeof(PluginFileData));
	FirstEntry.RealFilePos = sizeof(PluginHeader);		//adjust for header offset

	//first, allocate enough memory for the entries
	PluginEntries = (PluginFileData **)malloc(sizeof(PluginFileData*) * (priv_FileDataCount + 1));
	if(!PluginEntries)
		return deFALSE;

	//set all the positions for the pointers
	PluginEntries[0] = &FirstEntry;
	PluginEntries[1] = &priv_FileData[0];
	for(Counter=2; Counter <= priv_FileDataCount; Counter++)
		PluginEntries[Counter] = (PluginFileData*)((long)PluginEntries[Counter-1] + sizeof(PluginFileData));

	//bubblesort the pluginentries so that they are in order based on the file order
	BufferSize = 0;
	for(Counter = 1; Counter <= (priv_FileDataCount-1); Counter++)
	{
		for(Counter2 = Counter+1; Counter2 <= priv_FileDataCount; Counter2++)
		{
			//if the first entry is larger than the 2nd for position, swap
			if(PluginEntries[Counter]->RealFilePos > PluginEntries[Counter2]->RealFilePos)
			{
				//swap the entries based on file position
				TempPluginEntry = PluginEntries[Counter];
				PluginEntries[Counter] = PluginEntries[Counter2];
				PluginEntries[Counter2] = TempPluginEntry;
			}
		}

		//adjust the buffer size if need be, need the largest block
		if(BufferSize < PluginEntries[Counter]->Size)
			BufferSize = PluginEntries[Counter]->Size;
	}

	//check the last entry in the array for buffer size, counter points to the last entry
	if(BufferSize < PluginEntries[Counter]->Size)
		BufferSize = PluginEntries[Counter]->Size;

	//allocate a buffer
	TempBuffer = malloc(BufferSize);
	if(!TempBuffer)
	{
		free(PluginEntries);
		return deFALSE;
	}

	//update the counter until we are past the empty entries
	for(Counter=1; Counter <= priv_FileDataCount; Counter++)
	{
		//if we spot an entry with a position, exit
		if(PluginEntries[Counter]->RealFilePos)
			break;
	}

	//we now have a plugin list based on file position, shrink the file if possible
	//we start at the first entry with data
	for(; Counter <= priv_FileDataCount; Counter++)
	{
		//figure out where the end of the previous block is
		//entry 0 is a special setup to make sure we start data at position 0
		NeededPos = PluginEntries[Counter-1]->RealFilePos + PluginEntries[Counter-1]->Size;

		//see if this block lines up, if it is off, move it
		if(NeededPos != PluginEntries[Counter]->RealFilePos)
		{
			//next entry in line does not line up, move it
			priv_BaseFile->SetPosition(PluginEntries[Counter]->RealFilePos, deFile_Seek_Start);
			priv_BaseFile->Read(TempBuffer, PluginEntries[Counter]->Size);

			//now move to the correct location and write it
			priv_BaseFile->SetPosition(NeededPos, deFile_Seek_Start);
			priv_BaseFile->Write(TempBuffer, PluginEntries[Counter]->Size);

			//change the position in the data
			PluginEntries[Counter]->RealFilePos = NeededPos;
		}
	}

	//free our buffers
	free(PluginEntries);
	free(TempBuffer);

	return deTRUE;
}
