///////////////////////////////////////////////////////////////////////////////
/// @file deFSVirtual.cpp
///
/// @brief virtual 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 Nov 2001
/// @author Lightning
/// @remarks Creation
///
/// @date Dec 2002
/// @author kurifu
/// @remarks File searching handles
///
///////////////////////////////////////////////////////////////////////////////

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

#ifdef linux
#include <ctime>
#endif

#ifdef DE3D_CPU_LITTLE_ENDIAN
//a define for the ID of a virtual fs file. Done here as not user needed
#define VirtualID			0x53465644		//'DVFS', Destiny3D Virtual FS
#define VirtualVersion		0x00000001		//version of the VFS, current is 0.0.0.1
#else
//a define for the ID of a virtual fs file. Done here as not user needed
#define VirtualID			0x44564653		//'DVFS', Destiny3D Virtual FS
#define VirtualVersion		0x01000000		//version of the VFS, current is 0.0.0.1
#endif

//the size of the buffer used while moving data around (1MB)
#define MoveDataSize	(1024 * 1024)

deFSVirtual::deFSVirtual()
{
	//set everything to the defaults
	BaseFS = NULL;
	BaseFile = NULL;
	BaseFileOwner = deFALSE;
	BaseFlags = 0;

	//make sure no directory information exists
	RootDirectory = NULL;
	CurrentDirectory = NULL;
	CurDirectoryName = NULL;
}

deFSVirtual::~deFSVirtual()
{
	//closing a VFS, if anything is open, close it up
	if(BaseFile)
	{
		//we have a base fs still open, tell us to close it up
		CloseBaseFile();
	}

	//if a current directory name exists, free it
	if(CurDirectoryName)
		free(CurDirectoryName);

	//make sure it is all reset
	RootDirectory = NULL;
	CurrentDirectory = NULL;
	CurDirectoryName = NULL;
	BaseFile = NULL;
	BaseFileOwner = deFALSE;
	BaseFlags = 0;
}

void* deFSVirtual::GetInterface(IdeFileSystem::interface_t i)
{
	if (i == IdeFileSystem::iface_IdeFileSystem)
		return (IdeFileSystem*)(this);
	if (i == IdeFileSystem::iface_IdeFSVirtual)
		return (IdeFSVirtual*)(this);
	return NULL;
}

deFSVirtual::VirtualFileEntry *deFSVirtual::ParseDirectory(const char *Directory, deBoolean FileAttached, long *FileNameStart)
{
	long				LastChar;
	long				Counter;
	long				LastSlash;
	VirtualFileEntry *	CurDirectory;
	VirtualFileEntry *	CurSubDirectory;

	//this function will parse a directory out of a string and pass back the FileEntry or NULL for the directory
	//it will also create the directory if specified

	//if no directory, return null
	if(!Directory)
		return NULL;

	//figure out how much of the line needs to be parsed
	LastChar = strlen(Directory);

	//if a file is attached, cut it off
	if(FileAttached)
	{
		//if there is a filename on the end, then backtrack until Lastchar is 0, or we find a "/" or a "\"
		while(LastChar && Directory[LastChar - 1] != 0x2F && Directory[LastChar - 1] != 0x5C)
			LastChar--;

		//if a pointer exists, tell were the filename starts
		if(FileNameStart)
			*FileNameStart = LastChar;

		//if no last char, return the current directory
		if(!LastChar)
		{
			//see if the filename matches "." or "..", if it does then adjust
			//lastchar and return the directory accordingly
			if(memcmp(Directory, "..\0", 3) == 0)
			{
				//got a "..", return the parent
				if(FileNameStart)
					*FileNameStart = 2;

				//if no parent, fail
				if(!CurrentDirectory->ParentDirectory)
					return NULL;

				//return the parent
				return CurrentDirectory->ParentDirectory;
			}
			else if(memcmp(Directory, ".\0",2) == 0)
			{
				//got a ".", set the start accordingly
				if(FileNameStart)
					*FileNameStart = 1;

				//got a ".", return the current directory
				return CurrentDirectory;
			}
			else
				return CurrentDirectory;
		}
	}

	//set the last slash count to the first character
	LastSlash = 0;

	//if the first character is a "/" or a "\" then go with the root directory, otherwise the current directory
	if(Directory[0] == 0x2F || Directory[0] == 0x5C)
	{
		CurDirectory = RootDirectory;

		//first character is a slash, update LastSlash for it
		LastSlash++;
	}
	else
		CurDirectory = CurrentDirectory;

	//now, go thru the string parsing it up for the directories
	Counter = LastSlash + 1;
	while(Counter < LastChar)
	{
		//go thru, if a / or \ is found then move thru the directories
		while(Counter < LastChar && Directory[Counter] != 0x2F && Directory[Counter] != 0x5C)
			Counter++;

		//found the next directory

		//if a "." then do nothing, if a ".." then go to parent, otherwise find it
		if((Counter - LastSlash == 1) && (Directory[LastSlash] == 0x2E))
		{
			//if a "." then stay in the current directory
		}
		else if((Counter - LastSlash == 2) && (*((WORD*)&Directory[LastSlash]) == 0x2E2E))
		{
			//it is a "..", go to the parent if it exists
			if(!CurDirectory->ParentDirectory)
				return NULL;

			CurDirectory = CurDirectory->ParentDirectory;
		}
		else
		{
			//we have an actual file name to go with, try to find it in the sub directory list
			CurSubDirectory = CurDirectory->SubDirectory;
			while(CurSubDirectory)
			{
				//compare the names
				if(strnicmp(&Directory[LastSlash], CurSubDirectory->FileName, Counter - LastSlash) == 0)
				{
					//found it, set the current directory and exit the loop
					CurDirectory = CurSubDirectory;
					break;
				}

				//doesnt match, go onto the next entry
				CurSubDirectory = CurSubDirectory->NextDirectory;
			}

			//if we did not find the directory, then fail
			if(!CurSubDirectory)
				return NULL;
		}

		//move counter past the current slash
		Counter++;

		//update the LastSlash to point to the current character
		LastSlash = Counter;
	}

	//return the current directory
	return CurDirectory;	
}

IdeFile* deFSVirtual::Open(const char *Filename, long OpenFlags)
{
	deFileVirtual *		NewFile;

	//open up a virtual file

	//create the new file class
	NewFile = new deFileVirtual;
	if(!NewFile)
		return NULL;

	//set the basefs
	NewFile->priv_BaseFS = this;

	if(!Open(NewFile, Filename, OpenFlags))
	{
		NewFile->Release();
		return NULL;
	}

	//return the new file
	return NewFile;
}

deBoolean deFSVirtual::Open(deFileVirtual *BaseFile, const char *Filename, long OpenFlags)
{
	long				StartChar;
	VirtualFileEntry *	FileDirectory;
	VirtualFileEntry *	FileEntry;

	//open up a virtual file

	//make sure they can open the file in the way they want
	//check the read ability, dont do a direct compare as base can allow read
	//but the file opened without read
	if((OpenFlags & deFile_Open_Read) > (BaseFlags & deFile_Open_Read))
		return deFALSE;

	//check the write ability
	if((OpenFlags & deFile_Open_Write) > (BaseFlags & deFile_Open_Write))
		return deFALSE;

	//get the directory that the file is in and the starting position
	//in the Filename that the file starts
	FileDirectory = ParseDirectory(Filename, deTRUE, &StartChar);

	//if no file directory, fail
	if(!FileDirectory)
		return deFALSE;

	//now, find the file in the chain
	FileEntry = FileDirectory->NextFile;
	while(FileEntry)
	{
		//check the name
		if(stricmp(FileEntry->FileName, &Filename[StartChar]) == 0)
			break;

		//no match, go for the next entry
		FileEntry = FileEntry->NextFile;
	}

	//now, if no file and we are not to create a file, fail
	if(!FileEntry && !(OpenFlags & (deFile_Open_Create | deFile_Open_CreateAlways)))
		return deFALSE;

	//if a file and not to open an existing file or creating a new one, fail
	if(FileEntry && !(OpenFlags & (deFile_Open_Existing | deFile_Open_CreateAlways)))
		return deFALSE;
	
	if(!FileEntry && (OpenFlags & deFile_Open_Create))
	{
		//make sure the name is not in use as a directory
		if(NameInUse(FileDirectory, &Filename[StartChar], NULL))
			return deFALSE;

		//if they want to create a new entry, create it
		FileEntry = (VirtualFileEntry *)malloc(sizeof(*FileEntry));
		if(!FileEntry)
			return deFALSE;

		//clear it's contents
		memset(FileEntry, 0, sizeof(*FileEntry));

		//give it a name
		FileEntry->FileName = (char *)malloc(strlen(&Filename[StartChar]) + 1);
		if(!FileEntry->FileName)
		{
			free(FileEntry);
			return deFALSE;
		}

		//copy the name from the string, being it is at the end, we'll copy the null also
		memcpy(FileEntry->FileName, &Filename[StartChar], strlen(&Filename[StartChar]) + 1);

		//give it the parent
		FileEntry->ParentDirectory = FileDirectory;

		//and attach it
		FileEntry->NextFile = FileDirectory->NextFile;
		FileDirectory->NextFile = FileEntry;

		//set the file's position to the end of the file
		FileEntry->Position = BaseFile->GetSize();
	}

	//set the virtual file entry
	BaseFile->priv_CurrentFile = FileEntry;

	//set the file flags
	BaseFile->priv_FileFlags = OpenFlags;

	//make sure the file's current position is 0
	BaseFile->priv_CurrentPosition = 0;

	//if they want to overwrite a current file, change the file's size to 0
	if(OpenFlags & deFile_Open_CreateAlways)
	{
		//set the size to 0
		if(!SetSize(BaseFile, 0, NULL))
			return deFALSE;
	}

	//set the file entry's pointer to the class
	FileEntry->OpenedFile = BaseFile;

	//return the new file
	return deTRUE;
}

deBoolean deFSVirtual::Close(deFileVirtual *FileEntry)
{
	//close an opened file

	//if no current file, fail
	if(!FileEntry->priv_CurrentFile)
		return deFALSE;

#ifdef _WIN32
	//if the file was opened with write mode, adjust the time stamp
	if(FileEntry->priv_FileFlags & deFile_Open_Write)
		GetSystemTimeAsFileTime((LPFILETIME)&FileEntry->priv_CurrentFile->Time);
#elif linux
	//if the file was opened with write mode, adjust the time stamp
	if(FileEntry->priv_FileFlags & deFile_Open_Write)
  {
    time_t CurTime;
    time(&CurTime);
		FileEntry->priv_CurrentFile->Time = CurTime;
  }
#else
#error deFSVirtual::Close not written for os
#endif

	//erase the pointer to the class
	FileEntry->priv_CurrentFile->OpenedFile = NULL;

	//reset any data about a file
	FileEntry->priv_CurrentFile = NULL;
	FileEntry->priv_FileFlags = 0;
	FileEntry->priv_CurrentPosition = 0;
	
	return deTRUE;
}

long deFSVirtual::Read(deFileVirtual *FileEntry, void *Buffer, long Length)
{
	deFSVirtual::VirtualFileEntry *CurrentFile;

	//read data from an opened file

	//get the current file
	CurrentFile = FileEntry->priv_CurrentFile;

	//if no file is opened or no permission to read, return -1 for failure
	if(!CurrentFile || !(FileEntry->priv_FileFlags & deFile_Open_Read))
		return -1;

	//if no buffer or invalid length, fail
	if(!Buffer || Length < 0)
		return -1;

	//make sure length is not larger than the end of the file, if it is, adjust
	if(FileEntry->priv_CurrentPosition + Length > CurrentFile->Size)
		Length = CurrentFile->Size - FileEntry->priv_CurrentPosition;

	//set the position to read from
	if(BaseFile->SetPosition(CurrentFile->Position + FileEntry->priv_CurrentPosition, deFile_Seek_Start) == -1)
		return -1;

	//now, read into the buffer the data file
	return BaseFile->Read(Buffer, Length);
}

long deFSVirtual::Write(deFileVirtual *FileEntry, void *Buffer, long Length)
{
	deFSVirtual::VirtualFileEntry *CurrentFile;

	//write to an open file

	//get the current file
	CurrentFile = FileEntry->priv_CurrentFile;

	//if no file is opened or no permissiong to write, return -1 for failure
	if(!CurrentFile || !(FileEntry->priv_FileFlags & deFile_Open_Write))
		return -1;

	//if the data being written is going to cause the file to be larger, call SetSize to
	//adjust the file
	if(FileEntry->priv_CurrentPosition + Length > CurrentFile->Size)
	{
		//the file will be larger, adjust for it
		if(!SetSize(FileEntry, FileEntry->priv_CurrentPosition + Length, NULL))
			return -1;
	}

	//the file is the right size, move the BaseFile pointer to the write place
	if(BaseFile->SetPosition(CurrentFile->Position + FileEntry->priv_CurrentPosition, deFile_Seek_Start) == -1)
		return -1;

	//now write the data
	return BaseFile->Write(Buffer, Length);
}

//internal function for setting the size due to needing to pass along the callback
DWORD deFSVirtual::SetSize(deFileVirtual *FileEntry, long NewSize, deFSPROGRESS *StatusCallback)
{
	long			OldPosition;
	DWORD			StatusReturn;
	deFSVirtual::VirtualFileEntry*	CurrentFile;

	//change the size of a file

	//get the current file
	CurrentFile = FileEntry->priv_CurrentFile;

	//if no file, fail
	if(!CurrentFile)
		return deFSPROGRESS_CANCEL;

	//if the file's new size is smaller than old, call MoveData
	//otherwise, adjust the current pointer to the end of the file and call AdjustFile
	//as the file may need to be moved
	if(NewSize < CurrentFile->Size)
	{
		//new size is smaller, call MoveData to shift the data after the file
		StatusReturn = MoveData(CurrentFile->Position + CurrentFile->Size, CurrentFile->Size - NewSize, StatusCallback);

		//if the return is not continue, then return whatever the return is
		if(!(StatusReturn & deFSPROGRESS_CONTINUE))
			return StatusReturn;
	}
	else
	{
		//new size is larger. Store the current position, adjust to the end of the file
		//and call AdjustFile so it is larger
		OldPosition = FileEntry->priv_CurrentPosition;

		//set the current position to the end of the file
		FileEntry->priv_CurrentPosition = CurrentFile->Size;

		//call AdjustFile with the size we need to add to the file
		StatusReturn = AdjustFile(FileEntry, NewSize - CurrentFile->Size, deFALSE, StatusCallback);

		//restore the current position
		FileEntry->priv_CurrentPosition = OldPosition;

		//if the return is not continue, then return whatever the return is
		if(!(StatusReturn & deFSPROGRESS_CONTINUE))
			return StatusReturn;
	}

	//set the file's new size
	CurrentFile->Size = NewSize;

	//return that everything is fine
	return deFSPROGRESS_CONTINUE;
}

const char *deFSVirtual::GetDirectory()
{
	long				Counter;
	long				NameLen;
	VirtualFileEntry *	CurDirectory;

	//get the name of the current directory

	//if no current directory, fail
	if(!CurrentDirectory)
		return NULL;

	//if a current name exists, free it
	if(CurDirectoryName)
	{
		free(CurDirectoryName);
		CurDirectoryName = NULL;
	}

	//count how many characters we need
	CurDirectory = CurrentDirectory;
	Counter = 0;
	while(CurDirectory)
	{
		//add in the length of the name
		Counter = Counter + strlen(CurDirectory->FileName) + 1;

		//go to the parent entry
		CurDirectory = CurDirectory->ParentDirectory;
	}

	//if the counter is not 1, then we have more than the root directory. There is an extra
	//slash being counted so remove it
	if(Counter != 1)
		Counter--;

	//allocate memory for the name
	CurDirectoryName = (char *)malloc(Counter + 1);

	//set the last char to null
	CurDirectoryName[Counter] = 0x00;

	//copy in the names
	CurDirectory = CurrentDirectory;
	while(CurDirectory != RootDirectory)
	{
		//get the name's length
		NameLen = strlen(CurDirectory->FileName);

		//backtrack for the name
		Counter = Counter - NameLen;

		//copy the name
		memcpy(&CurDirectoryName[Counter], CurDirectory->FileName, NameLen);

		//add the slash
		Counter--;
		CurDirectoryName[Counter] = 0x5C;	//i want to use / but i know people will complain
											//so i went with the M$ layout

		//go up to the parent directory
		CurDirectory = CurDirectory->ParentDirectory;
	}

	//make sure the first char is a \ as we may be at the root directory
	CurDirectoryName[0] = 0x5C;

	//return the generated path
	return CurDirectoryName;
}

deBoolean deFSVirtual::SetDirectory(const char *Directory)
{
	VirtualFileEntry *	NewDir;

	//set the new current directory
	NewDir = ParseDirectory(Directory, deFALSE, NULL);

	//if no directory returned, fail as it wasnt found
	if(!NewDir)
		return deFALSE;

	//found the new directory, set it and return true
	CurrentDirectory = NewDir;
	return deTRUE;
}

deBoolean deFSVirtual::Rename(const char *OldFilename, const char *NewFilename)
{
	//rename a file

	//make sure the old name and new name dont have patterns in them
	if(IsPattern(OldFilename) || IsPattern(NewFilename))
		return deFALSE;

	//files can be renamed from 1 dir to another so call the move function
	return Move(OldFilename, NewFilename, NULL);
}

deBoolean deFSVirtual::Delete(const char *Filename, deFSPROGRESS StatusCallback)
{
	VirtualFileEntry *	CurEntry;
	VirtualFileEntry *	DirectoryEntry;
	VirtualFileEntry *	PrevEntry;
	long				NameStart;
	deFileProperties	FileProperties;
	char *				TempDirectory;
	DWORD				StatusReturn;
	deBoolean			DeletedFile;
	deFSFindHandle		FindHandle;
	deFileVirtual *		TempFile;

	//delete a file from a directory or matching sub directories

	//For each file that is deleted, BLOCKMOVE is returned per block of data
	//that is moved during the delete as the whole VFS has to be shifted to cover
	//up the space. The file's reported size and current size are actually the amount
	//of data that has to be shifted to cover up the old space.

	//make sure a Base FS exists and we can write to it
	if(!BaseFile || !(BaseFlags & deFile_Open_Write))
	{
		//let them know that the operation had a problem
		if(StatusCallback)
			StatusCallback(0, 0, deFSCALLBACK_ERRORFATAL, NULL, NULL);

		return deFALSE;
	}


	//get the directory the file is in
	DirectoryEntry = ParseDirectory(Filename, deTRUE, &NameStart);
	if(!DirectoryEntry)
	{
		//let them know that the operation had a problem
		if(StatusCallback)
			StatusCallback(0, 0, deFSCALLBACK_ERRORFATAL, NULL, NULL);

		return deFALSE;
	}

	//flag that nothing has been removed
	DeletedFile = deFALSE;

	//create the temp file
	TempFile = new deFileVirtual;

	//start a find for the stuff to delete
	PrevEntry = DirectoryEntry;
	FindHandle = FindFirst(Filename, &FileProperties);

	//loop until no more files found
	for(;; FindNext(FindHandle, &FileProperties))
	{
		//verify the find handle exists
		if(!FindHandle)
			break;

		//setup the default return
		StatusReturn = deFSPROGRESS_CONTINUE;

		//tell that a new file is starting
		if(StatusCallback)
			StatusReturn = StatusCallback(0, 0, deFSCALLBACK_STARTING, FileProperties.Filename, NULL);

		//if they dont want info, wipe the callback
		if(StatusReturn & deFSPROGRESS_QUIET)
			StatusCallback = NULL;

		//if they want to cancel the operation completely, exit the loop
		if(StatusReturn & deFSPROGRESS_CANCEL)
			break;

		//if they dont want to skip the file, then work on the file
		if(!(StatusReturn & deFSPROGRESS_CANCELCURRENT))
		{
			if(FileProperties.Flags & deFile_Directory)
			{
				//allocate memory for the directory path to remove
				TempDirectory = (char *)malloc(NameStart + strlen(FileProperties.Filename) + 1);
				if(!TempDirectory)
				{
					//let them know that the operation had a problem
					if(StatusCallback)
						StatusCallback(0, 0, deFSCALLBACK_ERRORFATAL, FileProperties.Filename, NULL);

					//erase the find data
					FindEnd(FindHandle);

					//release the temp file
					TempFile->Release();

					//return failure
					return deFALSE;
				}

				//setup the name
				memcpy(TempDirectory, Filename, NameStart);

				//copy the directory name + null
				memcpy(&TempDirectory[NameStart], FileProperties.Filename, strlen(FileProperties.Filename) + 1);

				//call the DeleteDirectory function to remove the directory
				if(!DeleteDirectory(TempDirectory, StatusCallback))
				{
					//dont tell that this failed as DeleteDirectory would have reported it
					//it will return true if the user wants to continue along

					//erase the find data
					FindEnd(FindHandle);

					//release the temp file
					TempFile->Release();

					//return failure
					return deFALSE;
				}

				//free the allocated memory
				free(TempDirectory);

				//flag that something was removed
				DeletedFile = deTRUE;
			}
			else
			{
				//loop thru until we find the file's entry, always start were we left
				//off as all previous files dont match
				CurEntry = PrevEntry->NextFile;
				while(CurEntry)
				{
					//see if we find the file
					if(CurEntry == ((FindInfoVirtual *)FindHandle)->FindLastEntry)
						break;

					//no match, next file
					PrevEntry = CurEntry;
					CurEntry = CurEntry->NextFile;
				}

				//if no current entry, fail. There SHOULD be an entry. The Find command found it
				if(!CurEntry)
				{
					//let them know that the operation had a problem. 
					if(StatusCallback)
						StatusCallback(0, 0, deFSCALLBACK_ERRORFATAL, FileProperties.Filename, NULL);

					//erase the find data
					FindEnd(FindHandle);

					//release the temp file
					TempFile->Release();

					//return failure
					return deFALSE;
				}

				//found the file, make sure it isn't open
				if(CurEntry->OpenedFile)
				{
					//set the default return incase no callback exists
					StatusReturn = deFSPROGRESS_CANCEL;

					//let them know that the operation had a problem
					if(StatusCallback)
						StatusReturn = StatusCallback(0, 0, deFSCALLBACK_ERROROPENED, FileProperties.Filename, NULL);

					//take action based on the return

					//if they want us quiet, then wipe out the callback entry
					if(StatusReturn & deFSPROGRESS_QUIET)
						StatusCallback = NULL;

					//stop the whole operation?
					if(StatusReturn & deFSPROGRESS_CANCEL)
					{
						//erase the find data
						FindEnd(FindHandle);

						//release the temp file
						TempFile->Release();

						//return failure
						return deFALSE;
					}

					//well, the action wasn't canceled, continue
					PrevEntry = CurEntry;	//modify the previous enty so this entry isnt done again
				}
				else
				{
					//resize the file

					//set the temp file
					TempFile->priv_CurrentFile = CurEntry;

					//set the file's size to 0
					StatusReturn = SetSize(TempFile, 0, &StatusCallback);

					//reset the temp file incase release is called
					TempFile->priv_CurrentFile = NULL;

					//check the return value to see what we are to be doing
					
					//see if they want to cancel the file
					if(StatusReturn & (deFSPROGRESS_CANCEL | deFSPROGRESS_CANCELCURRENT))
					{
						//stop the whole operation?
						if(StatusReturn & deFSPROGRESS_CANCEL)
						{
							//erase the find data
							FindEnd(FindHandle);

							//release the temp file
							TempFile->Release();

							//return failure
							return deFALSE;
						}

						//well, the action wasn't canceled, continue
						//modify the previous enty so this entry isnt done again
						PrevEntry = CurEntry;
					}
					else
					{
						//unlink it from the chain, PrevEntry is either a file entry
						//or the directory itself. Either way, the next file entry needs
						//to be modified
						PrevEntry->NextFile = CurEntry->NextFile;

						//now erase the memory for the file
						free(CurEntry->FileName);
						free(CurEntry);

						//flag that something was removed
						DeletedFile = deTRUE;
					}
				}
			}
		}

		//set the default return
		StatusReturn = deFSPROGRESS_CONTINUE;

		//tell the callback that a file finished. Because the entry is removed, we no longer have the name
		//so return NULL on the name
		if(StatusCallback)
			StatusReturn = StatusCallback(0, 0, deFSCALLBACK_FINISHED, NULL, NULL);

		//if they dont want more inf, wipe out the callback
		if(StatusReturn & deFSPROGRESS_QUIET)
			StatusCallback = NULL;

		//if they want to cancel the operation, exit the loop
		if(StatusReturn & deFSPROGRESS_CANCEL)
			break;

		//continue on
	}

    //release the temp file
	TempFile->Release();

	//erase the find data
	FindEnd(FindHandle);

	//return wether or not something was removed
	return DeletedFile;
}

#pragma warning (push)
#pragma warning (disable : 4100)
#pragma todo (Remove disable of warn 4100)
deBoolean deFSVirtual::Move(const char *OldFilename, const char *NewFilename, deFSPROGRESS StatusCallback)
{
	return deFALSE;
}

deBoolean deFSVirtual::Copy(const char *OldFilename, const char *NewFilename, deFSPROGRESS StatusCallback)
{
	return deFALSE;
}
#pragma warning (pop)

deBoolean deFSVirtual::AddDirectory(const char *Directory)
{
	VirtualFileEntry *	CurDirectory;
	VirtualFileEntry *	NewDirectory;
	long				Counter;
	long				NameLen;

	//add a directory

	//make sure a Base FS exists and we can write to it
	if(!BaseFile || !(BaseFlags & deFile_Open_Write))
		return deFALSE;

	//if no path or no length to it, fail
	if(!Directory || strlen(Directory) == 0)
		return deFALSE;

	//first, get the directory to add it to
	CurDirectory = ParseDirectory(Directory, deTRUE, &Counter);
	if(!CurDirectory)
		return deFALSE;

	//grab the length of the name
	NameLen = strlen(&Directory[Counter]) + 1;

	//make sure the name is not too long
	if(NameLen > 255)
		return deFALSE;

	//check and see if the name is in use
	if(NameInUse(CurDirectory, &Directory[Counter], NULL))
		return deFALSE;

	//the name is not in use, create a new directory entry for it
	NewDirectory = (VirtualFileEntry *)malloc(sizeof(*NewDirectory));
	if(!NewDirectory)
		return deFALSE;

	//clear the entry out
	memset(NewDirectory, 0, sizeof(*NewDirectory));

	//allocate memory for the name
	NewDirectory->FileName = (char *)malloc(NameLen);
	if(!NewDirectory->FileName)
	{
		free(NewDirectory);
		return deFALSE;
	}

	//copy the name
	memcpy(NewDirectory->FileName, &Directory[Counter], NameLen);

#ifdef _WIN32
	//set the time for the directory
	GetSystemTimeAsFileTime((LPFILETIME)&NewDirectory->Time);
#elif linux
	//set the time for the directory
  time_t CurTime;
  time(&CurTime);
	NewDirectory->Time = CurTime;
#else
#error deFSVirtual::AddDirectory not written for os
#endif

	//set the parent directory
	NewDirectory->ParentDirectory = CurDirectory;

	//add it to the chain
	NewDirectory->NextDirectory = CurDirectory->SubDirectory;
	CurDirectory->SubDirectory = NewDirectory;

	return deTRUE;
}

deBoolean deFSVirtual::DeleteDirectory(const char *Directory, deFSPROGRESS StatusCallback)
{
	//a function to call the internal function that needs the StatusCallback as a poitner to
	//a pointer
	
	//call the internal function, return wether or not the progress was to continue
	return (DeleteDirectory(Directory, &StatusCallback) == deFSPROGRESS_CONTINUE);
}

DWORD deFSVirtual::DeleteDirectory(const char *Directory, deFSPROGRESS *StatusCallback)
{
	VirtualFileEntry *		CurEntry;
	VirtualFileEntry *		OriginalEntry;
	VirtualFileEntry *		TempEntry;
	VirtualFileEntry *		RMDirectory;
	DWORD					StatusReturn;
	deFileVirtual *			TempFile;

	//delete the specified directory

	//make sure a Base FS exists and we can write to it
	if(!BaseFile || !(BaseFlags & deFile_Open_Write))
	{
		//set the default return
		StatusReturn = deFSPROGRESS_CONTINUE;

		//there is a problem, set a fatal error
		if(*StatusCallback)
			StatusReturn = (*StatusCallback)(0, 0, deFSCALLBACK_ERRORFATAL, Directory, NULL);

		//if they dont want more messages then wipe the callback out
		if(StatusReturn & deFSPROGRESS_QUIET)
			*StatusCallback = NULL;
		
		return deFSPROGRESS_CANCEL;
	}

	//make sure we were passed a directory to delete
	if(!Directory || strlen(Directory) == 0)
	{
		//set the default return
		StatusReturn = deFSPROGRESS_CONTINUE;

		//there is a problem, set a fatal error
		if(*StatusCallback)
			StatusReturn = (*StatusCallback)(0, 0, deFSCALLBACK_ERRORFATAL, Directory, NULL);

		//if they dont want more messages then wipe the callback out
		if(StatusReturn & deFSPROGRESS_QUIET)
			*StatusCallback = NULL;
		
		return deFSPROGRESS_CANCEL;
	}

	//get a pointer to the directory they want to delete
	RMDirectory = ParseDirectory(Directory, deFALSE, NULL);
	if(!RMDirectory)
	{
		//set the default return
		StatusReturn = deFSPROGRESS_CONTINUE;

		//there is a problem, set a fatal error
		if(*StatusCallback)
			StatusReturn = (*StatusCallback)(0, 0, deFSCALLBACK_ERRORFATAL, Directory, NULL);

		//if they dont want more messages then wipe the callback out
		if(StatusReturn & deFSPROGRESS_QUIET)
			*StatusCallback = NULL;
		
		return deFSPROGRESS_CANCEL;
	}

	//make sure they dont try to delete the root directory
	//or the current directory
	if((RMDirectory == RootDirectory) || (RMDirectory == CurrentDirectory))
	{
		//set the default return
		StatusReturn = deFSPROGRESS_CONTINUE;

		//there is a problem, set a fatal error
		if(*StatusCallback)
			StatusReturn = (*StatusCallback)(0, 0, deFSCALLBACK_ERRORFATAL, Directory, NULL);

		//if they dont want more messages then wipe the callback out
		if(StatusReturn & deFSPROGRESS_QUIET)
			*StatusCallback = NULL;

		return deFSPROGRESS_CANCEL;
	}

	//create the temp file
	TempFile = new deFileVirtual;

	//get the file list
	CurEntry = RMDirectory->NextFile;

	//go thru and wipe the files out
	TempEntry = NULL;
	while(CurEntry)
	{
		//tell them of the entry we are going to remove
		
		//set the default return
		StatusReturn = deFSPROGRESS_CONTINUE;

		//tell them of the entry
		if(*StatusCallback)
			StatusReturn = (*StatusCallback)(0, CurEntry->Size, deFSCALLBACK_STARTING, CurEntry->FileName, NULL);

		//if they dont want any more info then remove the callback
		if(StatusReturn & deFSPROGRESS_QUIET)
			*StatusCallback = NULL;

		//if they want to completely cancel then do so
		if(StatusReturn & deFSPROGRESS_CANCEL)
			return deFSPROGRESS_CANCEL;

		//if they want to continue the file then do so
		if(StatusReturn & deFSPROGRESS_CONTINUE)
		{
			//if the current entry is open, tell them about it
			if(CurEntry->OpenedFile)
			{
				//set the default return
				StatusReturn = deFSPROGRESS_CANCELCURRENT;

				//the file is opened, tell them about it
				if(*StatusCallback)
					StatusReturn = (*StatusCallback)(0, 0, deFSCALLBACK_ERROROPENED, Directory, NULL);

				//if they dont want more messages then wipe the callback out
				if(StatusReturn & deFSPROGRESS_QUIET)
					*StatusCallback = NULL;
				
				//if they want to cancel the whole progress then do so
				if(StatusReturn & deFSPROGRESS_CANCEL)
					return deFSPROGRESS_CANCEL;

				//they must want to cancel the current file or continue, skip the file
			}
			else
			{
				//tell the temp file what it is
				TempFile->priv_CurrentFile = CurEntry;

				//set the size of the file to 0
				StatusReturn = SetSize(TempFile, 0, StatusCallback);

				//unset it incase Release is called
				TempFile->priv_CurrentFile = NULL;

				//check the return

				//if they want to cancel then do so
				if(StatusReturn & deFSPROGRESS_CANCEL)
				{
					TempFile->Release();

					//return that we had a problem
					return deFSPROGRESS_CANCEL;
				}

				//if they want to continue then do so
				if(StatusReturn & deFSPROGRESS_CONTINUE)
				{
					//free the file's name
					free(CurEntry->FileName);

					//set the NextFile pointer to the next file
					if(TempEntry)
						TempEntry->NextFile = CurEntry->NextFile;
					else
						RMDirectory->NextFile = CurEntry->NextFile;

					//free the memory of the old entry
					free(CurEntry);
					CurEntry = NULL;

					//file was removed, let them know

					//set the default return
					StatusReturn = deFSPROGRESS_CONTINUE;

					//tell them of the entry
					if(*StatusCallback)
						StatusReturn = (*StatusCallback)(0, CurEntry->Size, deFSCALLBACK_FINISHED, CurEntry->FileName, NULL);

					//if they dont want any more info then remove the callback
					if(StatusReturn & deFSPROGRESS_QUIET)
						*StatusCallback = NULL;

					//if they want to completely cancel then do so
					if(StatusReturn & deFSPROGRESS_CANCEL)
					{
						TempFile->Release();
						return deFSPROGRESS_CANCEL;
					}
				}

				//if a current entry still exists then set TempEntry so we know where in the chain we are
				if(CurEntry)
					TempEntry = CurEntry;
			}
		}

		//set CurEntry to the next file. If TempEntry exists then it points to the last file not removed
		if(!TempEntry)
			CurEntry = RMDirectory->NextFile;
		else
			CurEntry = TempEntry->NextFile;
	}
	
	//release the temp file
	TempFile->Release();
	TempFile = NULL;

	//go thru and wipe out the sub directories

	//store the original directory we were on
	OriginalEntry = CurrentDirectory;

	//set the current directory to this directory
	CurrentDirectory = RMDirectory;

	//grab the sub directory list
	CurEntry = RMDirectory->SubDirectory;
	while(CurEntry)
	{
		//let them know of the directory that is going to be removed

		//set the default return
		StatusReturn = deFSPROGRESS_CONTINUE;

		//tell them of the entry
		if(*StatusCallback)
			StatusReturn = (*StatusCallback)(0, CurEntry->Size, deFSCALLBACK_STARTING, CurEntry->FileName, NULL);

		//if they dont want any more info then remove the callback
		if(StatusReturn & deFSPROGRESS_QUIET)
			*StatusCallback = NULL;

		//if they want to completely cancel then do so
		if(StatusReturn & deFSPROGRESS_CANCEL)
			return deFSPROGRESS_CANCEL;

		//if they want to continue, do so
		if(StatusReturn & deFSPROGRESS_CONTINUE)
		{
			//grab the next entry in the chain
			TempEntry = CurEntry->NextDirectory;

			//delete a sub directory
			StatusReturn = DeleteDirectory(CurEntry->FileName, StatusCallback);

			//if cancel is received then cancel the progress
			if(StatusReturn & deFSPROGRESS_CANCEL)
			{
				//something failed, fail
				CurrentDirectory = OriginalEntry;
				return deFSPROGRESS_CANCEL;
			}

			//if CancelCurrent is obtained, the sub directory was not removed from the chain
			if(StatusReturn & deFSPROGRESS_CANCELCURRENT)
			{
				//set CurEntry to the entry after it
				CurEntry = CurEntry->NextDirectory;
			}
			else
			{
				//on to the next sub directory, the DeleteDirectory
				//will free CurEntry and remove it from the directory
				//chain so just pull the next entry
				CurEntry = TempEntry;
			}

			//let them know the directory was removed

			//set the default return
			StatusReturn = deFSPROGRESS_CONTINUE;

			//tell them of the entry
			if(*StatusCallback)
				StatusReturn = (*StatusCallback)(0, 0, deFSCALLBACK_FINISHED, NULL, NULL);

			//if they dont want any more info then remove the callback
			if(StatusReturn & deFSPROGRESS_QUIET)
				*StatusCallback = NULL;

			//if they want to completely cancel then do so
			if(StatusReturn & deFSPROGRESS_CANCEL)
				return deFSPROGRESS_CANCEL;

		}
	}

	//restore the current directory
	CurrentDirectory = OriginalEntry;

	//if any directories or files exist then return CancelCurrent
	if(RMDirectory->SubDirectory || RMDirectory->NextFile)
		return deFSPROGRESS_CANCELCURRENT;

	//everything is gone, wipe the directory out

	//wipe out the RMDirectory's name
	free(RMDirectory->FileName);

	//remove RMDirectory from the chain
	CurEntry = RMDirectory->ParentDirectory->SubDirectory;
	OriginalEntry = NULL;
	while(CurEntry)
	{
		//if it matches, exit
		if(CurEntry == RMDirectory)
			break;

		//try the next entry
		OriginalEntry = CurEntry;
		CurEntry = CurEntry->NextDirectory;
	}

	//if no previous entry, set the sub directory, otherwise
	//set OriginalEntry's NextDirectory to bypass the one we are
	//removing
	if(!OriginalEntry)
		RMDirectory->ParentDirectory->SubDirectory = CurEntry->NextDirectory;
	else
		OriginalEntry->NextDirectory = CurEntry->NextDirectory;

	//free the directory's memory now
	free(CurEntry);

	//return to continue
	return deFSPROGRESS_CONTINUE;
}

// INPUT:
// X, NULL, NULL/X : This will end the present search, and release the handle and return null
// NULL, X, X : This will start a new search and return a pointer to a new handle
void* deFSVirtual::FindFirst(const char *Pattern, deFileProperties *Properties)
{
	FindInfoVirtual*		FindData;
	void *					Handle;
	long					FindPatternStart;
	
	//allocate memory for the find data
	FindData = (FindInfoVirtual*)malloc(sizeof(FindInfoVirtual));
	if(FindData == NULL )
		return NULL;

	//set it up
	memset(FindData, 0, sizeof(FindInfoVirtual));
	FindData->FindID = VirtualFindID;
	Handle = FindData;

	//find the start directory entry
	FindData->FindDirectory = ParseDirectory(Pattern, deTRUE, &FindPatternStart);
	if(!FindData->FindDirectory)
	{
		free(FindData);
		return NULL;
	}

	//allocate memory to copy the pattern
	FindData->FindPattern = (char *)malloc(strlen(&Pattern[FindPatternStart]) + 1);
	if(!FindData->FindPattern)
	{
		free(FindData);
		return NULL;
	}

	//now copy the pattern
	memcpy(FindData->FindPattern, &Pattern[FindPatternStart], strlen(&Pattern[FindPatternStart]) + 1);

	//go find a file
	if(!FindNext(Handle, Properties))
	{
		//couldnt find a file, fail
		FindEnd(Handle);
		return NULL;
	}

	//everything is fine, return the handle
	return Handle;
}

deBoolean deFSVirtual::FindNext(void* Handle, deFileProperties *Properties)
{
	FindInfoVirtual*		FindData;
	VirtualFileEntry *		CurFindEntry = NULL;

	//find a file in the current directory that matches the specified pattern

	//if no properties or no handle, fail
	if(!Handle || !Properties)
		return deFALSE;

	//reset the properties
	memset(Properties, 0, sizeof(*Properties));

	FindData = (FindInfoVirtual*)Handle;

	// valid structure?
	if(FindData->FindID != VirtualFindID)
		return deFALSE;

	//if we havent searched directories, then search them
	if(!FindData->FindDirSearched)
	{
		//see if any of the directory names match

		//if no last entry, start a new entry, otherwise continue
		if(!FindData->FindLastEntry)
			CurFindEntry = FindData->FindDirectory->SubDirectory;
		else
			CurFindEntry = FindData->FindLastEntry->NextDirectory;

		//go thru until we find an entry that matches
		while(CurFindEntry)
		{
			//if the name matches the pattern, exit the loop
			if(MatchPattern(FindData->FindPattern, CurFindEntry->FileName))
				break;

			//go to the next directory
			CurFindEntry = CurFindEntry->NextDirectory;
		}

		//if we hit the end, set the flag saying we went thru the dirs
		if(!CurFindEntry)
		{
			FindData->FindDirSearched = deTRUE;
			FindData->FindLastEntry = NULL;
		}
	}

	//if we have searched directories, then search the files
	//this is not an else for the above if as the above if
	//may not find a match and needs to look at files
	if(FindData->FindDirSearched)
	{
		//see if any files match

		//if no previous find entry, start from the beginning
		if(!FindData->FindLastEntry)
			CurFindEntry = FindData->FindDirectory->NextFile;
		else
			CurFindEntry = FindData->FindLastEntry->NextFile;

		//go thru the entries until we hit the end or find a match
		while(CurFindEntry)
		{
			//if the name matches the pattern, exit the loop
			if(MatchPattern(FindData->FindPattern, CurFindEntry->FileName))
				break;

			//go to the next file
			CurFindEntry = CurFindEntry->NextFile;
		}

		//if we hit the end, clean up and exit
		if(!CurFindEntry)
		{ 
			//clear out the find info
			free(FindData->FindPattern);

			//reset the data
			memset(&FindData, 0, sizeof(FindData));

			//make sure we dont lose the ID as FindClose needs to be called
			FindData->FindID = VirtualFindID;

			//return false to say we didnt find a match
			return deFALSE;
		}
	}

	//set the name and time if they want info
	if(Properties)
	{
		Properties->Filename = CurFindEntry->FileName;
		Properties->Time = CurFindEntry->Time;

		//if we searched the directories then set the file's data
		//otherwise we have a directory
		if(FindData->FindDirSearched)
			Properties->Size = CurFindEntry->Size;
		else
		{
			//got a directory, no size, and set the directory flag
			Properties->Size = 0;
			Properties->Flags = deFile_Directory;
		}
	}

	//set the last find to the current one
	FindData->FindLastEntry = CurFindEntry;

	//say everything is fine
	return deTRUE;
}

deBoolean deFSVirtual::FindEnd(void* Handle)
{
	FindInfoVirtual*	FindData;

	//end a find that was started

	//if no handle, fail
	if(!Handle)
		return deFALSE;

	//get the handle
	FindData = (FindInfoVirtual*)Handle;

	//if not the right ID, exit
	if(FindData->FindID != VirtualFindID)
		return deFALSE;

	//if a pattern exists, erase it
	if(FindData->FindPattern)
		free(FindData->FindPattern);

	//free it all
	free(FindData);
	return deTRUE;
}

deBoolean deFSVirtual::NameInUse(VirtualFileEntry *Directory, const char *Name, deBoolean *IsDirectory)
{
	VirtualFileEntry *	CurEntry;
	long				Counter;

	//check and see if a name is in use as a directory or file
	//return true if the name is found, otherwise return false

	//first, make sure the name is not a pattern. If it is, fail
	Counter = 0;
	while(Name[Counter] != 0x00)
	{
		//if we find any invalid chars, fail that the name is in use
		if( Name[Counter] == '*' ||
			Name[Counter] == '/' ||
			Name[Counter] == '?' ||
			Name[Counter] == '\\'
		  )
			return deTRUE;

		//next character
		Counter++;
	}

	//make sure the name is not . or ..
	//i dont use strcmp or memcmp as this is much quicker in compiled code
	if(*(WORD *)Name == 0x002E)
		return deTRUE;
	else if(*(WORD *)Name == 0x2E2E && Name[2] == 0x00)
		return deTRUE;

	//first, check the sub directories
	CurEntry = Directory->SubDirectory;
	while(CurEntry)
	{
		//check the names
		if(stricmp(CurEntry->FileName, Name) == 0)
		{
			//found it, if they want to know if the returned entry is a directory then flag
			if(IsDirectory)
				*IsDirectory = deTRUE;

			//return that we found it
			return deTRUE;
		}

		//no match, get the next entry
		CurEntry = CurEntry->NextDirectory;
	}

	//not found, check the files
	CurEntry = Directory->NextFile;
	while(CurEntry)
	{
		//check the names
		if(stricmp(CurEntry->FileName, Name) == 0)
		{
			//found it, if they want to know if the returned entry is a directory then flag
			if(IsDirectory)
				*IsDirectory = deFALSE;

			//return that we found it
			return deTRUE;
		}

		//no match, get the next entry
		CurEntry = CurEntry->NextFile;
	}

	//no match, fail
	return deFALSE;
}

deBoolean deFSVirtual::OpenBaseFile(IdeFile *BaseFile, long OpenFlags)
{
	VirtualHeader	HeaderInfo;
	
	//make sure no current base system is open, if one is, fail
	if(BaseFile)
		return deFALSE;

	//take a base file system and make sure the data it passes us is our own FS

	//make sure they are not mixing too many flags
	if((OpenFlags & deFile_Open_CreateAlways) && (OpenFlags & deFile_Open_Always))
		return deFALSE;		//fail, too many open flags passed

	//they want to open an existing or create it if it does not exist
	if((OpenFlags & deFile_Open_Always) == deFile_Open_Always)
	{
		//try to open it up as existing	
		if(!OpenBaseFile(BaseFile, OpenFlags & ~deFile_Open_Create))
		{
			//opening up as existing failed, try with a create
			if(!OpenBaseFile(BaseFile, OpenFlags & ~deFile_Open_Existing))
				return deFALSE;		//open with create failed, fail
		}

		//the open passed, return
		return deTRUE;
	}


	//see if they want a new FS or an existing
	if(OpenFlags & deFile_Open_Existing)
	{
		//open existing, dont create it

		//seek to the beginning of the file
		BaseFile->SetPosition(0, deFile_Seek_Start);

		//read the header for the file
		if(BaseFile->Read(&HeaderInfo, sizeof(HeaderInfo)) != sizeof(HeaderInfo))
			return deFALSE;		//couldnt read the whole header, fail

		//make sure the header is swapped properly
		HeaderInfo.DirectoryOffset = deByteOrderBE32(HeaderInfo.DirectoryOffset);

		//read the header, verify it
		if(HeaderInfo.ID != VirtualID || HeaderInfo.Version != VirtualVersion)
			return deFALSE;		//not our virtual file

		//make sure the directory pointer is inside the file
		if(HeaderInfo.DirectoryOffset > BaseFile->GetSize())
			return deFALSE;		//directory pointer is outside the file size

		//seek ahead to the directory structure and read it
		BaseFile->SetPosition(HeaderInfo.DirectoryOffset, deFile_Seek_Start);

		//set the Base FS
		BaseFile = BaseFile;

		//read the directory structure in
		if(!ReadDirectoryTree())
		{
			DestroyDirectoryTree();	//destroy any stored data
			BaseFile = NULL;			//remove the base FS
			return deFALSE;			//something failed
		}

		//if we are allowed to write, set the file size to the end before the directory structure
		if(OpenFlags & deFile_Open_Write)
			BaseFile->SetSize(HeaderInfo.DirectoryOffset);
	}
	else if((OpenFlags & deFile_Open_Create) || (OpenFlags & deFile_Open_CreateAlways))
	{
		//create it

		//make sure we can write to the FS
		if(!(OpenFlags & deFile_Open_Write))
			return deFALSE;		//no write flag, fail

		//set the file size to 0 just incase the file previously existed
		BaseFile->SetSize(0);

		//setup the header
		HeaderInfo.ID = VirtualID;
		HeaderInfo.Version = VirtualVersion;
		HeaderInfo.DirectoryOffset = 0;

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

		//setup the root entry
		RootDirectory = (VirtualFileEntry *)malloc(sizeof(*RootDirectory));
		if(!RootDirectory)
			return deFALSE;

		//blank the root directory out
		memset(RootDirectory, 0, sizeof(*RootDirectory));

		//allocate 1 byte of memory, enough for a null character for a blank name
		RootDirectory->FileName = (char *)malloc(1);
		if(!RootDirectory->FileName)
		{
			free(RootDirectory);
			return deFALSE;
		}

		//set the name to null
		RootDirectory->FileName[0] = 0x00;

#ifdef _WIN32
		//set the modify time
		GetSystemTimeAsFileTime((LPFILETIME)&RootDirectory->Time);
#elif linux
		//set the modify time
    time_t CurTime;
    time(&CurTime);
  	RootDirectory->Time = CurTime;
#else
#error deFSVirtual::OpenBaseFile not written for os
#endif
	}
	else
	{
		//They dont want to open for existing or for create, fail
		return deFALSE;
	}

	//everything passed, set a few base paramaters
	BaseFile = BaseFile;
	BaseFlags = OpenFlags;
	CurrentDirectory = RootDirectory;
	
	//everything is fine, return true
	return deTRUE;
}

deBoolean deFSVirtual::OpenBaseFilename(const char *Filename, long OpenFlags)
{
	deFSReal *		NewFS;
	IdeFile *		NewFile;

	//make sure no current base system is open, if one is, fail
	if(BaseFile)
		return deFALSE;

	//create a new base file
	NewFS = new deFSReal;
	if(!NewFS)
		return deFALSE;

	//open up the file with the flags, make sure we can read the file
	NewFile = NewFS->Open(Filename, OpenFlags | deFile_Open_Read);
	if(!NewFile)
	{
		delete NewFS;
		return deFALSE;
	}

	//see if the file is valid
	if(!OpenBaseFile(NewFile, OpenFlags))
	{
		//something went wrong, clean up
		NewFile->Release();
		delete NewFS;
		return deFALSE;
	}

	//save the basefs
	BaseFS = NewFS;

	//everything is fine, say we own the base FS
	BaseFileOwner = deTRUE;

	//return
	return deTRUE;
}

deBoolean deFSVirtual::CloseBaseFile()
{
	long			DirectoryOffset;
	VirtualHeader	Header;
	deBoolean		Ret;

	//close the base file

	Ret = deTRUE;

	//see if a base FS exists
	if(!BaseFile)
		return deFALSE;

	//if the file is for write, write out the directory tree data
	if(BaseFlags & deFile_Open_Write)
	{
		//seek to the end of the file. On open with read, the file is shrunk to the beginning of the directory data
		DirectoryOffset = BaseFile->SetPosition(0, deFile_Seek_End);

		//write the data
		CurrentDirectory = RootDirectory;
		if(!WriteDirectoryTree())
			Ret = deFALSE;

		//seek to the beginning
		BaseFile->SetPosition(0, deFile_Seek_Start);

		//setup the header data
		Header.ID = VirtualID;
		Header.Version = VirtualVersion;
		Header.DirectoryOffset = deByteOrderBE32(DirectoryOffset);

		//write the header
		if(BaseFile->Write(&Header, sizeof(Header)) != sizeof(Header))
			Ret = deFALSE;
	}

	//if we are the owner, close it and free it
	if(BaseFileOwner)
	{
		if(!BaseFile->Release())
			Ret = deFALSE;

		//if we owned it, see if we have a basefs to release
		if(BaseFS)
			BaseFS->Release();
	}

	//delete the tree
	CurrentDirectory = RootDirectory;
	DestroyDirectoryTree();	

	//reset the base stuff
	BaseFile = NULL;
	BaseFileOwner = deFALSE;
	BaseFlags = 0;

	//free any directory name that exists
	if(CurDirectoryName)
		free(CurDirectoryName);

	CurDirectoryName = NULL;

	//return true
	return Ret;
}

deBoolean deFSVirtual::ReadDirectoryTree()
{
	BYTE				NameSize;
	VirtualFileEntry *	NewEntry;
	VirtualFileEntry *	NewFileEntry;
	VirtualFileEntry *	CurrentFile;
	WORD				DirectoryCount;
	WORD				FileCount;
	DWORD				Counter;

	//we should already be in position to read the directories
	CurrentFile = NULL;
	
	//create a new file entry
	NewEntry = (VirtualFileEntry *)malloc(sizeof(*NewEntry));
	if(!NewEntry)
		return deFALSE;

	//blank it all out
	memset(NewEntry, 0, sizeof(*NewEntry));

	//read a byte for the name
	if(BaseFile->Read(&NameSize, sizeof(NameSize)) != sizeof(NameSize))
		return deFALSE;		//error reading the name length

	//if no name size and a root entry already exists, fail
	if(!NameSize && RootDirectory)
		return deFALSE;

	//allocate memory for the name
	NewEntry->FileName = (char *)malloc(NameSize + 1);
	if(!NewEntry->FileName)
		return deFALSE;

	//set the last byte of the name to null
	NewEntry->FileName[NameSize] = 0x00;

	//if a name size, fill in the name
	if(NameSize)
	{
		//read the name in
		if(BaseFile->Read(NewEntry->FileName, NameSize) != NameSize)
			return deFALSE;	//error reading the name
	}

	//read the directory and file counts
	if(BaseFile->Read(&DirectoryCount, sizeof(DirectoryCount)) != sizeof(DirectoryCount))
		return deFALSE;

	if(BaseFile->Read(&FileCount, sizeof(FileCount)) != sizeof(FileCount))
		return deFALSE;

	//read the last modified time for the file
	if(BaseFile->Read(&NewEntry->Time, sizeof(NewEntry->Time)) != sizeof(NewEntry->Time))
		return deFALSE;

	//swap the bytes
	DirectoryCount = deByteOrderBE16(DirectoryCount);
	FileCount = deByteOrderBE16(FileCount);
	NewEntry->Time = deByteOrderBE64(NewEntry->Time);

	//set the new entry's parent directory
	NewEntry->ParentDirectory = CurrentDirectory;

	//if any file, add us to it, otherwise create it
	//we use current file to point to the current directory in the sub directory chain
	//for the parent
	if(CurrentFile)
	{
		CurrentFile->NextDirectory = NewEntry;
	}
	else
	{
		//need to set the current directory's sub directory entry
		if(CurrentDirectory)
			CurrentDirectory->SubDirectory = NewEntry;
		else
		{
			//need to create the current directory and the root directory
			RootDirectory = NewEntry;
			CurrentDirectory = NewEntry;
		}
	}

	//set the current file to nothing
	CurrentFile = NULL;

	//set the current directory to us
	CurrentDirectory = NewEntry;

	//loop thru and read the directories
	for(Counter = 0; Counter < DirectoryCount; Counter++)
	{
		//grab a sub directory data
		if(!ReadDirectoryTree())
			return deFALSE;
	}

	//loop thru and read our files
	CurrentFile = NULL;
	for(Counter = 0; Counter < FileCount; Counter++)
	{
		//allocate memory for a new file entry
		NewFileEntry = (VirtualFileEntry *)malloc(sizeof(*NewFileEntry));
		if(!NewFileEntry)
			return deFALSE;

		//clear the entry out
		memset(NewFileEntry, 0, sizeof(*NewFileEntry));
		
		//read the file name length
		if(BaseFile->Read(&NameSize, sizeof(NameSize)) != sizeof(NameSize))
			return deFALSE;

		//if no size for the name, fail
		if(!NameSize)
			return deFALSE;

		//allocate memory for the filename
		NewFileEntry->FileName = (char *)malloc(NameSize + 1);
		if(!NewFileEntry->FileName)
			return deFALSE;

		//read the name
		if(BaseFile->Read(NewFileEntry->FileName, NameSize) != NameSize)
			return deFALSE;

		//null terminate it
		NewFileEntry->FileName[NameSize] = 0x00;

		//read in the position, size, and time of the file. They follow in order in the file as they do in the memory structure
		//so do 1 read to save time
		#define ReadPosSizeTime_Size sizeof(NewFileEntry->Position) + sizeof(NewFileEntry->Size) + sizeof(NewFileEntry->Time)
		if(BaseFile->Read(&NewFileEntry->Position, ReadPosSizeTime_Size) != ReadPosSizeTime_Size)
			return deFALSE;

		//swap the position, size, and time
		NewFileEntry->Position = deByteOrderBE32(NewFileEntry->Position);
		NewFileEntry->Size = deByteOrderBE32(NewFileEntry->Size);
		NewFileEntry->Time = deByteOrderBE64(NewFileEntry->Time);

		//set the parent directory for the file
		NewFileEntry->ParentDirectory = CurrentFile;
		
		//add the new file to the file chain
		if(!CurrentFile)
			NewEntry->NextFile = NewFileEntry;	//no current file, add it to the directory as the first
		else
			CurrentFile->NextFile = NewFileEntry;	//add it to the chain

		//set the chain to the new entry so the next one is added to it
		CurrentFile = NewFileEntry;
	}

	//reset the current file and current directory
	CurrentFile = NewEntry;
	CurrentDirectory = NewEntry->ParentDirectory;

	//if we are the root entry that is exiting, set the current file to NULL
	if(NewEntry == RootDirectory)
		CurrentFile = NULL;
	
	//return true
	return deTRUE;
}

deBoolean deFSVirtual::WriteDirectoryTree()
{
	BYTE					NameLen;
	WORD					DirectoryCount;
	WORD					FileCount;
	s64						TempTime;
	VirtualFileEntry *		CurEntry;
	VirtualFileEntry *		OriginalEntry;
	VirtualFileEntry		TempEntry;

	//write the directory tree entries to the current position in the base file

	//if no current directory, fail
	if(!CurrentDirectory)
		return deFALSE;

	//get the name length of the directory
	NameLen = (BYTE)strlen(CurrentDirectory->FileName);

	//if no name and not the root directory, fail
	if(!NameLen && CurrentDirectory != RootDirectory)
		return deFALSE;

	//write out the length of the name
	if(BaseFile->Write(&NameLen, sizeof(NameLen)) != sizeof(NameLen))
		return deFALSE;

	//write out the name itself if it exists
	if(NameLen)
	{
		if(BaseFile->Write(CurrentDirectory->FileName, NameLen) != NameLen)
			return deFALSE;
	}

	//obtain the count of sub directory and files
	CurEntry = CurrentDirectory->SubDirectory;
	DirectoryCount = 0;
	while(CurEntry)
	{
		DirectoryCount++;
		CurEntry = CurEntry->NextDirectory;
	}

	//obtain the number of files
	CurEntry = CurrentDirectory->NextFile;
	FileCount = 0;
	while(CurEntry)
	{
		FileCount++;
		CurEntry = CurEntry->NextFile;
	}

	//swap the bytes
	DirectoryCount = deByteOrderBE16(DirectoryCount);
	FileCount = deByteOrderBE16(FileCount);

	//write the directory and file count out
	if(BaseFile->Write(&DirectoryCount, sizeof(DirectoryCount)) != sizeof(DirectoryCount))
		return deFALSE;

	if(BaseFile->Write(&FileCount, sizeof(FileCount)) != sizeof(FileCount))
		return deFALSE;

	TempTime = deByteOrderBE64(CurrentDirectory->Time);

	//write out the timestamp for the current directory
	if(BaseFile->Write(&TempTime, sizeof(TempTime)) != sizeof(TempTime))
		return deFALSE;

	//now, write out the sub directories
	CurEntry = CurrentDirectory->SubDirectory;
	OriginalEntry = CurrentDirectory;
	while(CurEntry)
	{
		//set the current directory to the next sub directory
		CurrentDirectory = CurEntry;

		//set CurEntry to the sub directory after this one
		CurEntry = CurEntry->NextDirectory;

		//call the write function to write the sub directory out
		if(!WriteDirectoryTree())
			return deFALSE;
	}

	//now write out the files
	CurEntry = OriginalEntry->NextFile;
	while(CurEntry)
	{
		//get the length of the name
		NameLen = (BYTE)strlen(CurEntry->FileName);

		//if no name, fail
		if(!NameLen)
			return deFALSE;

		//write out the filename length
		if(BaseFile->Write(&NameLen, sizeof(NameLen)) != sizeof(NameLen))
			return deFALSE;

		//write out the name
		if(BaseFile->Write(CurEntry->FileName, NameLen) != NameLen)
			return deFALSE;

		//write out the position, size, and time of the file. They follow in order in the file as they do in the memory structure
		//so do 1 write to save time
		#define WritePosSizeTime_Size sizeof(CurEntry->Position) + sizeof(CurEntry->Size) + sizeof(CurEntry->Time)

//if not a big edian setup, then byte swap
#if DE3D_CPU_LITTLE_ENDIAN
		//first, copy from the current entry, into our temp entry
		memcpy(&TempEntry.Position, &CurEntry->Position, WritePosSizeTime_Size);

		//swap the bytes if need be
		TempEntry.Position = deByteOrderBE32(TempEntry.Position);
		TempEntry.Size = deByteOrderBE32(TempEntry.Size);
		TempEntry.Time = deByteOrderBE64(TempEntry.Time);

		if(BaseFile->Write(&TempEntry.Position, WritePosSizeTime_Size) != WritePosSizeTime_Size)
			return deFALSE;
#else
		//write the data out
		if(BaseFile->Write(&CurEntry->Position, WritePosSizeTime_Size) != WritePosSizeTime_Size)
			return deFALSE;
#endif

		//go to the next file
		CurEntry = CurEntry->NextFile;
	}

	//return true
	return deTRUE;
}

deBoolean deFSVirtual::DestroyDirectoryTree()
{
	VirtualFileEntry *	CurEntry;
	VirtualFileEntry *	CurDirEntry;

	//destroy any memory used by a vfs

	if(!CurrentDirectory)
		return deFALSE;
	
	//loop thru and destroy all the files
	CurEntry = CurrentDirectory->NextFile;
	CurrentDirectory->NextFile = NULL;		//set the file list to NULL
	while(CurEntry)
	{
		//save the current file temporarily
		CurDirEntry = CurEntry;

		//go to the next file
		CurEntry = CurEntry->NextFile;

		//free the current one
		if(CurDirEntry->FileName)
			free(CurDirEntry->FileName);

		free(CurDirEntry);
	}

	CurDirEntry = CurrentDirectory;

	//now that all the files are free'd, free the sub directories
	CurrentDirectory = CurrentDirectory->SubDirectory;
	CurDirEntry->SubDirectory = NULL;	//make sure the sub directories is set to NULL
	while(CurrentDirectory)
	{
		if(!DestroyDirectoryTree())
			return deFALSE;

		//free the current entry and go on to the next one
		CurEntry = CurrentDirectory;
		CurrentDirectory = CurrentDirectory->NextDirectory;

		//free the name
		if(CurEntry->FileName)
			free(CurEntry->FileName);

		//free the directory entry
		free(CurEntry);
	}

	//set the current directory to the one passed to us
	CurrentDirectory = CurDirEntry;

	//now, if the curdirentry is the root entry, free it
	if(CurDirEntry == RootDirectory)
	{
		free(RootDirectory->FileName);
		free(RootDirectory);
		RootDirectory = NULL;
		CurrentDirectory = NULL;
	}

	//return true
	return deTRUE;
}

DWORD deFSVirtual::AdjustFile(deFileVirtual *FileEntry, long WriteSize, deBoolean MakeCopy, deFSPROGRESS *StatusCallback)
{
	void *				MoveBuffer;
	long				Counter;
	long				NewPosition;
	long				TempPosition;
	long				MoveSize;
	DWORD				StatusReturn;
	deFSPROGRESS		DummyProgress;
	VirtualFileEntry *	CurrentFile;

	//move a file if need be in a VFS. always returns true unless
	//something failed during a move.

	//WriteSize is the number of bytes that are going to be written to
	//the current position

	CurrentFile = FileEntry->priv_CurrentFile;

	//if no file, fail
	if(!CurrentFile)
		return deFSPROGRESS_CANCEL;

	//if WriteSize is smaller than 0, fail
	if(WriteSize < 0)
		return deFSPROGRESS_CANCEL;

	//if they dont want to make a copy of the file, do some checks to see
	//if we really have to move it
	if(!MakeCopy)
	{
		//check and see if the bytes being written are within the file, if so
		//return true
		if(FileEntry->priv_CurrentPosition + WriteSize <= CurrentFile->Size)
			return deFSPROGRESS_CONTINUE;

		//if the file's position is at the end of the VFS, return true
		if(CurrentFile->Position + CurrentFile->Size == BaseFile->GetSize())
			return deFSPROGRESS_CONTINUE;
	}

	//if a file has a size of 0, point it to the end of the VFS and exit
	//this code can be reached if the file was created earilier and shrunk
	if(CurrentFile->Size == 0)
	{
		//if they want a copy, dont adjust this one
		if(!MakeCopy)
			CurrentFile->Position = BaseFile->GetSize();

		//return
		return deFSPROGRESS_CONTINUE;
	}

	//if no callback exists then point it to the dummy callback
	if(!StatusCallback)
	{
		//set the dummy to null
		DummyProgress = NULL;

		//and set the pointer to the dummy
		StatusCallback = &DummyProgress;
	}

	//set the default status return
	StatusReturn = deFSPROGRESS_CONTINUE;

	//if a callback exists, let them know of the size of the file
	if(*StatusCallback)
		StatusReturn = (*StatusCallback)(0, CurrentFile->Size, deFSCALLBACK_BLOCKMOVED, NULL, NULL);

	//if they want us to be quiet, remove the callback
	if(StatusReturn & deFSPROGRESS_QUIET)
		*StatusCallback = NULL;

	//blank out the new position
	NewPosition = 0;

	//set the temporary position in the file
	TempPosition = 0;

	//if no cancel is done, then copy or move the data
	if(!(StatusReturn & (deFSPROGRESS_CANCEL | deFSPROGRESS_CANCELCURRENT)))
	{
		//the file is not at the end of the VFS and they want to write data
		//that would result in going past the end of the file. Move the file to the end
		//update the VFS entry for the new position, then call MoveData to shift
		//all the data over the old position

		//grab the file's size, allocate a MoveDatasize of memory or the file's size,
		//whichever is smaller
		Counter = CurrentFile->Size;
		if(Counter > MoveDataSize)
			Counter = MoveDataSize;

		//allocate the memory
		MoveBuffer = malloc(Counter);
		if(!MoveBuffer)
		{
			//set the default return
			StatusReturn = 0;

			//alert to a fatal error
			if(*StatusCallback)
				StatusReturn = (*StatusCallback)(0, CurrentFile->Size, deFSCALLBACK_ERRORFATAL, NULL, NULL);

			//if they dont want more messages then set the flag as so
			if(StatusReturn & deFSPROGRESS_QUIET)
				*StatusCallback = NULL;

			return deFSPROGRESS_CANCEL;
		}

		//get the new position
		NewPosition = BaseFile->GetSize();

		//divide Counter by MoveDataSize to find out how many MoveDataSize chunks to read/write
		Counter = Counter / MoveDataSize;

		//loop thru and move the chunks
		MoveSize = MoveDataSize;
		do
		{
			while(Counter)
			{
				//set the read position
				if(BaseFile->SetPosition(CurrentFile->Position + TempPosition, deFile_Seek_Start) == -1)
				{
					//set the default return
					StatusReturn = 0;

					//critical error, tell the callback about it if it exists
					if(*StatusCallback)
						StatusReturn = (*StatusCallback)(TempPosition, CurrentFile->Size, deFSCALLBACK_ERRORFATAL, NULL, NULL);

					//if they dont want more messages, wipe the callback out
					if(StatusReturn & deFSPROGRESS_QUIET)
						*StatusCallback = NULL;

					free(MoveBuffer);
					return deFSPROGRESS_CANCEL;
				}

				//read the data
				if(BaseFile->Read(MoveBuffer, MoveSize) != MoveSize)
				{
					//set the default return
					StatusReturn = 0;

					//critical error, tell the callback about it if it exists
					if(*StatusCallback)
						StatusReturn = (*StatusCallback)(TempPosition, CurrentFile->Size, deFSCALLBACK_ERRORFATAL, NULL, NULL);

					//if they dont want more messages, wipe the callback out
					if(StatusReturn & deFSPROGRESS_QUIET)
						*StatusCallback = NULL;

					free(MoveBuffer);
					return deFSPROGRESS_CANCEL;
				}

				//set the write position
				if(BaseFile->SetPosition(NewPosition + TempPosition, deFile_Seek_Start) == -1)
				{
					//set the default return
					StatusReturn = 0;

					//critical error, tell the callback about it if it exists
					if(*StatusCallback)
						StatusReturn = (*StatusCallback)(TempPosition, CurrentFile->Size, deFSCALLBACK_ERRORFATAL, NULL, NULL);

					//if they dont want more messages, wipe the callback out
					if(StatusReturn & deFSPROGRESS_QUIET)
						*StatusCallback = NULL;

					free(MoveBuffer);
					return deFSPROGRESS_CANCEL;
				}

				//write the data
				if(BaseFile->Write(MoveBuffer, MoveSize) != MoveSize)
				{
					//set the default return
					StatusReturn = 0;

					//critical error, tell the callback about it if it exists
					if(*StatusCallback)
						StatusReturn = (*StatusCallback)(TempPosition, CurrentFile->Size, deFSCALLBACK_ERRORFATAL, NULL, NULL);

					//if they dont want more messages, wipe the callback out
					if(StatusReturn & deFSPROGRESS_QUIET)
						*StatusCallback = NULL;

					free(MoveBuffer);
					return deFSPROGRESS_CANCEL;
				}

				//increment the position
				TempPosition = TempPosition + MoveSize;

				//decrement the counter
				Counter--;

				//set the default return for the callback
				StatusReturn = deFSPROGRESS_CONTINUE;

				//tell the callback about the data move
				if(*StatusCallback)
					StatusReturn = (*StatusCallback)(TempPosition, CurrentFile->Size, deFSCALLBACK_BLOCKMOVED, NULL, NULL);

				//if they dont want more data, unset the callback
				if(StatusReturn & deFSPROGRESS_QUIET)
					*StatusCallback = NULL;

				//if they want to cancel, then exit the loop
				if(StatusReturn & (deFSPROGRESS_CANCEL | deFSPROGRESS_CANCELCURRENT))
					break;
			}

			//check and see if the file status is canceled, if it is then cancel it
			if(StatusReturn & (deFSPROGRESS_CANCEL | deFSPROGRESS_CANCELCURRENT))
				break;

			//move the last bit of the file now

			//set the MoveSize to the left over size of the file
			MoveSize = CurrentFile->Size - TempPosition;

			//set the counter so the while loop will hit, only need to copy data once
			Counter = 1;

			//now, loop again to move the last bit of the file
		} while(MoveSize > 0);

		//free the memory used
		free(MoveBuffer);

		//add to TempPosition the file's old position so it now sits where the old file ends
		TempPosition = TempPosition + CurrentFile->Position;

		if(!MakeCopy)
		{
			//a copy of the file is not being made, adjust the file's size

			//set the file's new position to where we wrote it
			CurrentFile->Position = NewPosition;

			//now, call MoveData to move all the data and files from the old position to the new one
			return MoveData(TempPosition, CurrentFile->Size, StatusCallback);
		}
	}
	else
	{
		//if a new position exists, then reset the size of the base fs
		if(NewPosition)
		{
			if(!BaseFile->SetSize(NewPosition))
			{
				//set the default return
				StatusReturn = 0;

				//critical error, tell the callback about it if it exists
				if(*StatusCallback)
					StatusReturn = (*StatusCallback)(TempPosition, CurrentFile->Size, deFSCALLBACK_ERRORFATAL, NULL, NULL);

				//if they dont want more messages, wipe the callback out
				if(StatusReturn & deFSPROGRESS_QUIET)
					*StatusCallback = NULL;

				//the resize failed, make sure everything is canceled
				StatusReturn = deFSPROGRESS_CANCEL;
			}
		}
	}

	//return if the operation was canceled or not
	return StatusReturn;
}

DWORD deFSVirtual::MoveData(long StartPosition, long ByteShift, deFSPROGRESS *StatusCallback)
{
	long				DataSize;
	long				BlockSize;
	long				CurrentPosition;
	long				TotalSize;
	void *				Data;
	DWORD				StatusReturn;
	deFSPROGRESS		DummyCallback;

	//a function to actually shift data in a VFS

	//any data after StartPosition is shifted ByteShift bytes overwriting what is before it
	//the file is then shrunk to the new size

	//if byte shift is 0, return true
	if(!ByteShift)
		return deFSPROGRESS_CONTINUE;

	//make sure the start position and byte shift are positive values and make sure
	//that the amount being shifted doesnt result in a negative offset
	if(StartPosition < 0 || ByteShift < 0 || (StartPosition - ByteShift) < 0)
		return deFSPROGRESS_CONTINUE;

	//figure out how much data we have to move
	TotalSize = BaseFile->GetSize() - StartPosition;

	//if no callback exists then point it to the dummy callback
	if(!StatusCallback)
	{
		//set the dummy to null
		DummyCallback = NULL;

		//and set the pointer to the dummy
		StatusCallback = &DummyCallback;
	}

	//make sure we arent shifting to a larger size
	if(TotalSize < 0)
	{
		//got a bad size, report it
		
		//set the default return
		StatusReturn = deFSPROGRESS_CONTINUE;

		//alert to the problem
		if(*StatusCallback)
			StatusReturn = (*StatusCallback)(0, 0, deFSCALLBACK_ERRORFATAL, NULL, NULL);

		//if they dont want more messages, wipe the callback out
		if(StatusReturn & deFSPROGRESS_QUIET)
			*StatusCallback = NULL;

		//fail
		return deFSPROGRESS_CANCEL;
	}

	//if no data to shift, adjust the file's size and return true
	//this would occur if a file is shrunk when at the end of the VFS
	if(!TotalSize)
	{
		if(!BaseFile->SetSize(StartPosition - ByteShift))
		{
			//set the default return
			StatusReturn = deFSPROGRESS_CONTINUE;

			//alert them to the size problem and fail
			if(*StatusCallback)
				StatusReturn = (*StatusCallback)(0, 0, deFSCALLBACK_ERRORFATAL, NULL, NULL);

			//if they dont want more info, wipe the callback out
			if(StatusReturn & deFSPROGRESS_QUIET)
				*StatusCallback = NULL;

			return deFSPROGRESS_CANCEL;
		}

		return deFSPROGRESS_CONTINUE;
	}

	//set the position to start reading from
	if(BaseFile->SetPosition(StartPosition, deFile_Seek_Start) == -1)
	{
		//set the default return
		StatusReturn = deFSPROGRESS_CONTINUE;

		//alert them to the size problem and fail
		if(*StatusCallback)
			StatusReturn = (*StatusCallback)(0, 0, deFSCALLBACK_ERRORFATAL, NULL, NULL);

		//if they dont want more info, wipe the callback out
		if(StatusReturn & deFSPROGRESS_QUIET)
			*StatusCallback = NULL;

		return deFSPROGRESS_CANCEL;
	}

	//set blockSize to the TotalSize or MoveDataSize, whichever is smaller
	if(TotalSize < MoveDataSize)
		BlockSize = TotalSize;
	else
		BlockSize = MoveDataSize;

	//allocate memory for the move
	Data = malloc(BlockSize);
	if(!Data)
	{
		//set the default return
		StatusReturn = deFSPROGRESS_CONTINUE;

		//alert them to the size problem and fail
		if(*StatusCallback)
			StatusReturn = (*StatusCallback)(0, 0, deFSCALLBACK_ERRORFATAL, NULL, NULL);

		//if they dont want more info, wipe the callback out
		if(StatusReturn & deFSPROGRESS_QUIET)
			*StatusCallback = NULL;

		return deFSPROGRESS_CANCEL;
	}

	//setup the default return value
	StatusReturn = deFSPROGRESS_CONTINUE;

	//alert them to moving the actual VFS data
	if(*StatusCallback)
		StatusReturn = (*StatusCallback)(0, TotalSize, deFSCALLBACK_VFSLARGEMOVE, NULL, NULL);

	//if they want us to stop telling them info, then remove the callback
	if(StatusReturn & deFSPROGRESS_QUIET)
		*StatusCallback = NULL;

	//we do not check for cancel of the operation as this operation HAS to finish or will
	//have a fatal error, one or the other

	//figure out how many multiples of MoveDataSize we need to move
	DataSize = TotalSize / MoveDataSize;

	//move the data
	CurrentPosition = 0;
	do
	{
		while(DataSize)
		{
			//read a block of data
			if(BaseFile->Read(Data, BlockSize) != BlockSize)
			{
				free(Data);

				//set the default return
				StatusReturn = deFSPROGRESS_CONTINUE;

				//alert them to the size problem and fail
				if(*StatusCallback)
					StatusReturn = (*StatusCallback)(CurrentPosition, TotalSize, deFSCALLBACK_ERRORFATAL, NULL, NULL);

				//if they dont want more info, wipe the callback out
				if(StatusReturn & deFSPROGRESS_QUIET)
					*StatusCallback = NULL;

				return deFSPROGRESS_CANCEL;
			}
			
			//backtrack the right number of bytes
			if(BaseFile->SetPosition(-ByteShift - BlockSize, deFile_Seek_Current) == -1)
			{
				free(Data);

				//set the default return
				StatusReturn = deFSPROGRESS_CONTINUE;

				//alert them to the size problem and fail
				if(*StatusCallback)
					StatusReturn = (*StatusCallback)(CurrentPosition, TotalSize, deFSCALLBACK_ERRORFATAL, NULL, NULL);

				//if they dont want more info, wipe the callback out
				if(StatusReturn & deFSPROGRESS_QUIET)
					*StatusCallback = NULL;

				return deFSPROGRESS_CANCEL;
			}

			//write the data
			if(BaseFile->Write(Data, BlockSize) != BlockSize)
			{
				free(Data);

				//set the default return
				StatusReturn = deFSPROGRESS_CONTINUE;

				//alert them to the size problem and fail
				if(*StatusCallback)
					StatusReturn = (*StatusCallback)(CurrentPosition, TotalSize, deFSCALLBACK_ERRORFATAL, NULL, NULL);

				//if they dont want more info, wipe the callback out
				if(StatusReturn & deFSPROGRESS_QUIET)
					*StatusCallback = NULL;

				return deFSPROGRESS_CANCEL;
			}

			//setup for the next read
			if(BaseFile->SetPosition(ByteShift, deFile_Seek_Current) == -1)
			{
				free(Data);

				//set the default return
				StatusReturn = deFSPROGRESS_CONTINUE;

				//alert them to the size problem and fail
				if(*StatusCallback)
					StatusReturn = (*StatusCallback)(CurrentPosition, TotalSize, deFSCALLBACK_ERRORFATAL, NULL, NULL);

				//if they dont want more info, wipe the callback out
				if(StatusReturn & deFSPROGRESS_QUIET)
					*StatusCallback = NULL;

				return deFSPROGRESS_CANCEL;
			}

			//decrement the DataSize
			DataSize--;

			//update the current position to allow the callback to know
			CurrentPosition = CurrentPosition + BlockSize;

			//set the default return
			StatusReturn = deFSPROGRESS_CONTINUE;

			//alert them to moving the actual VFS data
			if(*StatusCallback)
				StatusReturn = (*StatusCallback)(CurrentPosition, TotalSize, deFSCALLBACK_BLOCKMOVED, NULL, NULL);

			//if they want us to stop telling them info, then remove the callback
			if(StatusReturn & deFSPROGRESS_QUIET)
				*StatusCallback = NULL;
		}

		//set BlockSize to the number of bytes left. It should be 0 when we are finished
		//copying
		BlockSize = TotalSize - CurrentPosition;
		DataSize = 1;

	} while(BlockSize > 0);

	//free the used memory
	free(Data);

	//now, adjust the file's size
	if(!BaseFile->SetSize(BaseFile->GetSize() - ByteShift))
	{
		//set the default return
		StatusReturn = deFSPROGRESS_CONTINUE;

		//something went wrong, let them know
		if(*StatusCallback)
			StatusReturn = (*StatusCallback)(CurrentPosition, TotalSize, deFSCALLBACK_ERRORFATAL, NULL, NULL);

		//if they dont want more messages, remove the callback
		if(StatusReturn & deFSPROGRESS_QUIET)
			*StatusCallback = NULL;

		//fail
		return deFSPROGRESS_CANCEL;
	}
	
	//now, go thru the file chain and update any file that has a position after StartPosition
	//and adjust it by ByteShift
	if(!AdjustFilePositions(RootDirectory, StartPosition, ByteShift))
	{
		//something went wrong, alert them
		if(*StatusCallback)
			StatusReturn = (*StatusCallback)(CurrentPosition, TotalSize, deFSCALLBACK_ERRORFATAL, NULL, NULL);

		//if they dont want more messages, remove the callback
		if(StatusReturn & deFSPROGRESS_QUIET)
			*StatusCallback = NULL;

		//fail
		return deFSPROGRESS_CANCEL;
	}
	else
		return deFSPROGRESS_CONTINUE;
}

deBoolean deFSVirtual::AdjustFilePositions(VirtualFileEntry *Directory, long StartPosition, long ByteShift)
{
	VirtualFileEntry *		CurFile;
	VirtualFileEntry *		CurDirectory;

	//a function to adjust file positions

	//if not directory, fail
	if(!Directory)
		return deFALSE;

	//check the current directory files
	CurFile = Directory->NextFile;
	while(CurFile)
	{
		//if the file's position is larger or equal to start position, adjust it
		if(CurFile->Position >= StartPosition)
			CurFile->Position = CurFile->Position - ByteShift;

		//next file
		CurFile = CurFile->NextFile;
	}

	//now, process the sub directories
	CurDirectory = Directory->SubDirectory;
	while(CurDirectory)
	{
		if(!AdjustFilePositions(CurDirectory, StartPosition, ByteShift))
			return deFALSE;

		//next sub directory
		CurDirectory = CurDirectory->NextDirectory;
	}

	//all done, return true
	return deTRUE;
}

deBoolean deFSVirtual::MatchPattern(const char *Pattern, const char *Filename)
{
	long	PatternCounter;
	long	FileCounter;

	//match a specified pattern to the filename

	//start doing a match
	PatternCounter = 0;
	FileCounter = 0;

	//loop until we hit the end of the file AND pattern
	//if we hit the end of 1 but not the other, then we fail a compare in the loop
	while(Filename[FileCounter] != 0x00 || Pattern[PatternCounter] != 0x00)
	{
		if(Pattern[PatternCounter] == '*')
		{
			//match a range of characters

			//loop until we find the next match for the pattern
			for(;;)
			{
				//call ourselves to do a compare, this way a match of
				//a*c will work on abcabc instead of finding abc and failing
				//due to extra letters after it. Example
				//a*c, first run finds ab, no match
				//abc, doesnt match, extra chars in name
				//abca, doesnt match
				//abcab, doesnt match
				//abcabc, matches, no extra chars
				if(MatchPattern(&Pattern[PatternCounter + 1], &Filename[FileCounter]) == deTRUE)
					break;

				//if we hit the end of the file, fail
				//compare here as * could be at the end of the pattern so check null to null
				if(Filename[FileCounter] == 0x00)
					return deFALSE;

				//did not match, go to the next entry in the filename
				FileCounter++;
			}

			//found a match, backup the FileCounter by 1 as we were matching a letter
			//ahead on the pattern. We could bump pattern ahead but then if matched on
			//the null's, we would increment past it and the while loop would not end
			//when it should
			FileCounter--;
		}
		else if(Pattern[PatternCounter] == '?')
		{
			//match one character to anything

			//make sure we dont match to a null
			if(Filename[FileCounter] == 0x00)
				return deFALSE;

		}
		else
		{
			//match a regular character

			//if no match, fail
			if(Filename[FileCounter] != Pattern[PatternCounter])
				return deFALSE;
		}

		//increase both the pattern and filename counters
		FileCounter++;
		PatternCounter++;
	}

	//passed the loop, return true that the pattern matches
	return deTRUE;
}

deBoolean deFSVirtual::IsPattern(const char *Pattern)
{
	long	Counter;

	//return wether or not the string really contains a pattern or not
	Counter = 0;
	while(Pattern[Counter] != 0x00)
	{
		//if we find a ? or a * then it is a pattern
		if(Pattern[Counter] == '?' || Pattern[Counter] == '*')
			return deTRUE;

		//next character
		Counter++;
	}

	//no pattern
	return deFALSE;
}
