#include "vmc.h"


//  IO fonctions


//----------------------------------------------------------------------------
// Format a vmc file previously mounted with fileXioMount(.
// mode can be FORMAT_FULL which mean erase all pages before formatting.
// or FORMAT_FAST which skip erasing all pages before formatting.
//----------------------------------------------------------------------------
int Vmc_Format ( iop_file_t *f, const char *dev, const char *blockdev, void *arg, size_t arglen ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: format %d\n", f->unit );

	int mode = FORMAT_FULL;

	struct direntry dirent;
	int             all_sector;
	char           *mcbuffer, *mcbuffer2;
	unsigned int    i, j, k, x, y, last_blk_sector, Page_Num, Page_Num2;

	Page_Num = 0;
	Page_Num2 = 0;

	if ( g_Vmc_Image[ f->unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	PROF_START ( vmc_formatProf ) 

	if ( mode == FORMAT_FULL ) 
	{

		for ( i = 0; i < g_Vmc_Image[ f->unit ].total_pages  /  g_Vmc_Image[ f->unit ].header.pages_per_block; i++ ) 
		{

			DEBUGPRINT ( 7, "vmcfs: format erasing block %d / %d\r", i, g_Vmc_Image[ f->unit ].total_pages  /  g_Vmc_Image[ f->unit ].header.pages_per_block );
			eraseBlock ( g_Vmc_Image[ f->unit ].fd, i );

		}

	}

	mcbuffer  = ( char* )malloc (  ( g_Vmc_Image[ f->unit ].header.page_size + 0xFF ) & ~( unsigned int )0xFF );
	mcbuffer2 = ( char* )malloc (  ( g_Vmc_Image[ f->unit ].header.page_size + 0xFF ) & ~( unsigned int )0xFF );

	DEBUGPRINT ( 3, "vmcfs: format start\n" );

	memset ( mcbuffer, g_Vmc_Image[ f->unit ].erase_byte, g_Vmc_Image[ f->unit ].header.page_size );
	memcpy ( mcbuffer, &g_Vmc_Image[ f->unit ].header, sizeof ( struct superblock )  );

	//  Write header page
	writePage ( g_Vmc_Image[ f->unit ].fd, mcbuffer, Page_Num );

	Page_Num++;

	DEBUGPRINT ( 3, "vmcfs: format indirect fat cluster\n" );

	//  goto fat table
	for ( i = 1; i < ( g_Vmc_Image[ f->unit ].header.indir_fat_clusters[ 0 ]* g_Vmc_Image[ f->unit ].header.pages_per_cluster ); i++, Page_Num++ ) { }

	all_sector      = 0;
	last_blk_sector = g_Vmc_Image[ f->unit ].header.indir_fat_clusters[ 0 ];

	DEBUGPRINT ( 3, "vmcfs: format fat table\n" );

	//  Write fat table pages
	for ( x = 0; g_Vmc_Image[ f->unit ].header.indir_fat_clusters[ x ]!= 0; x++ ) 
	{

		last_blk_sector = g_Vmc_Image[ f->unit ].header.indir_fat_clusters[ 0 ]+ x;
		Page_Num        = last_blk_sector * g_Vmc_Image[ f->unit ].header.pages_per_cluster;

		for ( i = x * g_Vmc_Image[ f->unit ].header.pages_per_cluster; ( i < (  ( x + 1 ) * g_Vmc_Image[ f->unit ].header.pages_per_cluster )  ) && ( i <= (  ( g_Vmc_Image[ f->unit ].total_pages - 1 )  /  65536 )  ); i++, Page_Num++ ) 
		{

			memset ( mcbuffer, g_Vmc_Image[ f->unit ].erase_byte, g_Vmc_Image[ f->unit ].header.page_size );

			for ( j = i * (  ( g_Vmc_Image[ f->unit ].header.page_size  /  2 )  /  g_Vmc_Image[ f->unit ].header.pages_per_cluster ), k = 0; ( j < i * (  ( g_Vmc_Image[ f->unit ].header.page_size  /  2 )  /  g_Vmc_Image[ f->unit ].header.pages_per_cluster ) + (  ( g_Vmc_Image[ f->unit ].header.page_size  /  2 )  /  g_Vmc_Image[ f->unit ].header.pages_per_cluster )  ) && ( j < (  ( g_Vmc_Image[ f->unit ].header.last_allocatable  /  ( g_Vmc_Image[ f->unit ].header.page_size  /  2 )  ) + 1 )  ); j++, k++ ) 
			{

				 (  ( unsigned int* ) mcbuffer )[ k ]= (  ( g_Vmc_Image[ f->unit ].total_pages - 1 )  /  65536 ) + last_blk_sector + 1 + j;
					
				memset ( mcbuffer2, g_Vmc_Image[ f->unit ].erase_byte, g_Vmc_Image[ f->unit ].header.page_size );
				Page_Num2 = (  ( unsigned int* ) mcbuffer )[ k ]* g_Vmc_Image[ f->unit ].header.pages_per_cluster;

				for ( y = 0; ( y < (  ( g_Vmc_Image[ f->unit ].header.page_size  /  2 )  /  g_Vmc_Image[ f->unit ].header.pages_per_cluster )  ) && ( all_sector < g_Vmc_Image[ f->unit ].header.last_allocatable ); y++, all_sector++ ) 
				{

					if ( y == 0 && j == 0 && i == 0 ) 
					{

						 (  ( unsigned int* ) mcbuffer2 )[ y ]= EOF_CLUSTER;

					}
					else
					{

						 (  ( unsigned int* ) mcbuffer2 )[ y ]= FREE_CLUSTER;

					}

				}

				writePage ( g_Vmc_Image[ f->unit ].fd, mcbuffer2, Page_Num2 );

				Page_Num2++;
				memset ( mcbuffer2, g_Vmc_Image[ f->unit ].erase_byte, g_Vmc_Image[ f->unit ].header.page_size );

				for ( y = 0; ( y < (  ( g_Vmc_Image[ f->unit ].header.page_size  /  2 )  /  g_Vmc_Image[ f->unit ].header.pages_per_cluster )  ) && ( all_sector < g_Vmc_Image[ f->unit ].header.last_allocatable ); y++, all_sector++ ) 
				{

					 (  ( unsigned int* ) mcbuffer2 )[ y ]= FREE_CLUSTER;

				}

				writePage ( g_Vmc_Image[ f->unit ].fd, mcbuffer2, Page_Num2 );

			}

			writePage ( g_Vmc_Image[ f->unit ].fd, mcbuffer, Page_Num );

		}

	}

	DEBUGPRINT ( 3, "vmcfs: format root directory\n" );

	Page_Num = g_Vmc_Image[ f->unit ].header.first_allocatable * g_Vmc_Image[ f->unit ].header.pages_per_cluster;
	
	memset ( &dirent, 0, sizeof ( dirent )  );

	dirent.mode      = DF_EXISTS | DF_0400 | DF_DIRECTORY | DF_READ | DF_WRITE | DF_EXECUTE; // 0x8427
	dirent.length    = 2;
	dirent.name[ 0 ] = '.';
	getPs2Time ( &dirent.created  );
	getPs2Time ( &dirent.modified );

	writePage ( g_Vmc_Image[ f->unit ].fd, ( unsigned char* ) &dirent, Page_Num );
	Page_Num++;

	memset ( &dirent, 0, sizeof ( dirent )  );

	dirent.mode      = DF_EXISTS | DF_HIDDEN | DF_0400 | DF_DIRECTORY | DF_WRITE | DF_EXECUTE; // 0xa426;
	dirent.length    = 0;
	dirent.name[ 0 ] = '.';
	dirent.name[ 1 ] = '.';
	getPs2Time ( &dirent.created  );
	getPs2Time ( &dirent.modified );

	writePage ( g_Vmc_Image[ f->unit ].fd, ( unsigned char* ) &dirent, Page_Num );

	free ( mcbuffer  );
	free ( mcbuffer2 );

	DEBUGPRINT ( 3, "vmcfs: format finished\n" );

	PROF_END ( vmc_formatProf ) 

	return VMCFS_ERR_NO;

}


//----------------------------------------------------------------------------
// Open a file from vmc image. fileXioOpen("vmc...
//----------------------------------------------------------------------------
int Vmc_Open ( iop_file_t *f, const char *path1, int flags, int mode ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: Open %i %s\n", f->unit, path1 );

	if ( g_Vmc_Image[ f->unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ f->unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_openProf ) 

	struct direntry       dirent;
	struct file_privdata* fprivdata = malloc ( sizeof ( struct file_privdata ) );

	if ( fprivdata == NULL )
		return -1;

	f->privdata = fprivdata;

	fprivdata->gendata.fd                = g_Vmc_Image[ f->unit ].fd;
	fprivdata->gendata.first_allocatable = g_Vmc_Image[ f->unit ].header.first_allocatable;
	fprivdata->gendata.last_allocatable  = g_Vmc_Image[ f->unit ].header.last_allocatable;

	memcpy ( fprivdata->gendata.indir_fat_clusters, g_Vmc_Image[ f->unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

	unsigned int dirent_cluster = getDirentryFromPath ( &dirent, path1, &( fprivdata->gendata ), f->unit );

	if ( dirent_cluster == ROOT_CLUSTER )
	{

		free ( fprivdata ); //  Release the allocated memory

		PROF_END ( vmc_openProf ) 

		return -1;	//  File not found, could not be opened.

	}
	else if ( dirent_cluster == NOFOUND_CLUSTER )
	{

		if ( flags & O_CREAT ) 
		{

			DEBUGPRINT ( 2, "vmcfs: Open with O_CREAT.\n");

			struct direntry parent;
			unsigned int    parent_cluster = 0;

			char* path = malloc ( strlen ( path1 ) + 1 ); //  + 1 for null terminator
			memcpy ( path, path1, strlen ( path1 ) + 1 ); //  create a local copy to work with

			char* filename = strrchr ( path, '/' ); //  last occurance of  / , which should split the file name from the folders

			//  essentially splits path into 2 strings
			filename[ 0 ]= '\0';
			filename++;

			DEBUGPRINT ( 2, "vmcfs: Creating file %s in parent folder %s\n", filename, path );

			memset ( &dirent, 0, sizeof ( dirent )  );

			// fill direntry file information
			strcpy ( dirent.name, filename );
			dirent.length  = 0;
			dirent.cluster = EOF_CLUSTER;
			dirent.mode    = DF_EXISTS | DF_0400 | DF_FILE | DF_READ | DF_WRITE | DF_EXECUTE; //  0x8417
			getPs2Time ( &dirent.created  );
			getPs2Time ( &dirent.modified );

			//  we need to know this directories entry in its parent...
			if ( path[ 0 ] == '\0' ) 
			{

				//  get the root directories entry
				parent_cluster = getDirentryFromPath ( &parent, ".", &( fprivdata->gendata ), f->unit );

			}
			else
			{

				//  get the folder entry for the parent
				parent_cluster = getDirentryFromPath ( &parent, path, &( fprivdata->gendata ), f->unit );

			}

			if ( parent_cluster == NOFOUND_CLUSTER )
			{

				DEBUGPRINT ( 3, "vmcfs: Unable to find parent directory.\n" );

				free ( path );
				free ( fprivdata ); //  Release the allocated memory

				PROF_END ( vmc_openProf ) 

				return -1;

			}

			dirent_cluster = addObject ( &( fprivdata->gendata ), parent_cluster, &parent, &dirent, f->unit );

			if ( dirent_cluster == ERROR_CLUSTER )
			{

				DEBUGPRINT ( 2, "vmcfs: open failed on %s\n", path1 );

				free ( path );
				free ( fprivdata ); //  Release the allocated memory

				PROF_END ( vmc_openProf ) 

				return -1;	//  File not found, could not be opened.

			}

			free ( path );

		}
		else
		{

			free ( fprivdata ); //  Release the allocated memory

			PROF_END ( vmc_openProf ) 

			return -1;	//  File not found, could not be opened.

		}

	}

	if ( ( ! ( dirent.mode & DF_EXISTS ) ) || ( flags & O_TRUNC ) ) // File found but is hidden, or trunc the file
	{

		if ( flags & O_TRUNC )
			DEBUGPRINT ( 3, "vmcfs: Open with O_TRUNC.\n");

		int          first_cluster   = 1;
		unsigned int current_cluster = 0;
		unsigned int last_cluster    = dirent.cluster;

		DEBUGPRINT ( 4, "vmcfs: Searching last cluster of file ...\n" );

		while ( 1 ) 
		{

			current_cluster = getFatEntry ( fprivdata->gendata.fd, last_cluster, fprivdata->gendata.indir_fat_clusters, FAT_VALUE );

			if ( current_cluster == FREE_CLUSTER ) 
			{

				// FREE_CLUSTER mean last cluster of the hidden file is found
				DEBUGPRINT ( 8, "vmcfs: Last cluster of file at %u\n", last_cluster );
			
				break;

			}
			else if ( current_cluster == EOF_CLUSTER ) 
			{

				// EOF_CLUSTER mean last cluster of the file is found
				DEBUGPRINT ( 8, "vmcfs: EOF_CLUSTER found.\n" );

				setFatEntry ( fprivdata->gendata.fd, last_cluster, FREE_CLUSTER, fprivdata->gendata.indir_fat_clusters, FAT_SET );

				break;

			}
			else
			{

				DEBUGPRINT ( 8, "vmcfs: Testing cluster %u ... value is %u\n", last_cluster, current_cluster );

				// Otherwise set first cluster as eof and remaining clusters as free
				if ( first_cluster == 1 )
				{

					setFatEntry ( fprivdata->gendata.fd, last_cluster, EOF_CLUSTER, fprivdata->gendata.indir_fat_clusters, FAT_SET );

					dirent.length  = 0;
					dirent.cluster = EOF_CLUSTER;
					dirent.mode    = DF_EXISTS | DF_0400 | DF_FILE | DF_READ | DF_WRITE | DF_EXECUTE; //  0x8417
					getPs2Time ( &dirent.created  );
					getPs2Time ( &dirent.modified );
					writePage ( fprivdata->gendata.fd, ( unsigned char* ) &dirent, ( dirent_cluster + fprivdata->gendata.first_allocatable ) * g_Vmc_Image[ f->unit ].header.pages_per_cluster + fprivdata->gendata.dirent_page );

					first_cluster = 0;

				}
				else
				{

					setFatEntry ( fprivdata->gendata.fd, last_cluster, FREE_CLUSTER, fprivdata->gendata.indir_fat_clusters, FAT_SET );

				}

			}

			last_cluster = current_cluster;

		}

	}

	// fill opened file informations
	fprivdata->file_position     = 0;
	fprivdata->file_dirpage      = ( dirent_cluster + fprivdata->gendata.first_allocatable ) * g_Vmc_Image[ f->unit ].header.pages_per_cluster + fprivdata->gendata.dirent_page;
	fprivdata->file_cluster      = dirent.cluster;
	fprivdata->cluster_offset    = 0;
	fprivdata->file_startcluster = dirent.cluster;
	fprivdata->file_length       = dirent.length; //  set the length to what it should be, and go from there

	DEBUGPRINT ( 2, "vmcfs: File %s opened with length %u\n", path1, fprivdata->file_length );

	PROF_END ( vmc_openProf ) 

	return 1;

}


//----------------------------------------------------------------------------
// Close a file previously open with fileXioOpen("vmc...
//----------------------------------------------------------------------------
int Vmc_Close (  iop_file_t* f ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: Close %i\n", f->unit );

	if ( g_Vmc_Image[ f->unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ f->unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_closeProf ) 

	struct file_privdata* fprivdata = f->privdata;

	free ( fprivdata );

	PROF_END ( vmc_closeProf ) 

	return 0;

}


//----------------------------------------------------------------------------
// Read a file previously open with fileXioOpen("vmc...
//----------------------------------------------------------------------------
int Vmc_Read (  iop_file_t* f, void* buffer, int size ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: Read %d bytes\n", size );

	if ( g_Vmc_Image[ f->unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ f->unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_readProf ) 

	if ( ! ( f->mode & O_RDONLY )  ) 
	{

		PROF_END ( vmc_readProf ) 

		return -1; //  if the file isn't opened for reading, return -1. No return eroor code from errno

	}

	struct file_privdata* fprivdata = f->privdata;

	fprivdata->gendata.first_allocatable = g_Vmc_Image[ f->unit ].header.first_allocatable;
	fprivdata->gendata.last_allocatable  = g_Vmc_Image[ f->unit ].header.last_allocatable;

	memcpy ( fprivdata->gendata.indir_fat_clusters, g_Vmc_Image[ f->unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

	//  we are at eof
	if (  ( fprivdata->file_length - fprivdata->file_position ) == 0 ) 
	{

		PROF_END ( vmc_readProf ) 

		return 0;

	}

	DEBUGPRINT ( 3, "vmcfs: Reading in %i bytes\n", size );

	//  we need to read in size, unless size is larger then length
	if ( size > fprivdata->file_length - fprivdata->file_position ) 
	{

		DEBUGPRINT ( 3, "vmcfs: Adjusting size to read in to be %u\n", fprivdata->file_length - fprivdata->file_position );

		size = fprivdata->file_length - fprivdata->file_position;

	}

	//  Ok so we have been requested to read size bytes from the open file, which starts with fprivdata->file_cluster
	//  and our current position in the cluster is cluster_offset, while our overall position is file_position.
	//  We can determine what cluster in the link we should be in, by taking our overall position and dividing by 1024
	//  the size of a cluster.

	int data_read = 0;  //  How much data we have read in so far
	char* cluster_data; //  Temporary storage for a cluster of data

	cluster_data  = ( char* )malloc ( ( g_Vmc_Image[ f->unit ].cluster_size + 0xFF ) & ~( unsigned int )0xFF );	
	memset ( cluster_data, 0, g_Vmc_Image[ f->unit ].cluster_size );

	//  While we still have data to read in
	while ( data_read < size ) 
	{

		//  Read in the file_cluster
		readCluster ( fprivdata->gendata.fd, cluster_data, fprivdata->file_cluster + fprivdata->gendata.first_allocatable );

		//  We have 1024 bytes in cluster_data now, but we already read in cluster_offset bytes
		//  So we now need to copy whats left into the buffer that was passed to us

		DEBUGPRINT ( 3, "vmcfs: There are %i bytes remaining\n", size - data_read );
 		DEBUGPRINT ( 3, "vmcfs: There are %i bytes left in cluster %u\n", ( fprivdata->cluster_offset == 0 ) ? 0 : g_Vmc_Image[ f->unit ].cluster_size - fprivdata->cluster_offset, fprivdata->file_cluster + fprivdata->gendata.first_allocatable );

		//  If the data remaining in a cluster is larger then the buffer passed to us, we can only read in size data
		if ( size - data_read < g_Vmc_Image[ f->unit ].cluster_size - fprivdata->cluster_offset ) 
		{

			DEBUGPRINT ( 3, "vmcfs: Read in %i bytes\n", size );

			memcpy ( buffer + data_read, cluster_data + fprivdata->cluster_offset, size - data_read );
			fprivdata->cluster_offset += ( size - data_read );
			data_read                 += ( size - data_read );

		}
		else	//  We can copy in the rest of the cluster, since there is space in the buffer passed to us
		{

			DEBUGPRINT ( 3, "vmcfs: Read in %i bytes\n", g_Vmc_Image[ f->unit ].cluster_size - fprivdata->cluster_offset );

			memcpy ( buffer + data_read, cluster_data + fprivdata->cluster_offset, g_Vmc_Image[ f->unit ].cluster_size - fprivdata->cluster_offset );
			data_read                 += ( g_Vmc_Image[ f->unit ].cluster_size - fprivdata->cluster_offset );
			fprivdata->cluster_offset += ( g_Vmc_Image[ f->unit ].cluster_size - fprivdata->cluster_offset );

		}
		
		//  If we have used up all of the data in a cluster, we need to tell it that the current cluster is the next one in the list
		if ( fprivdata->cluster_offset == g_Vmc_Image[ f->unit ].cluster_size ) 
		{

			DEBUGPRINT ( 6, "vmcfs: Moving onto next cluster\n" );

			fprivdata->file_cluster   = getFatEntry ( fprivdata->gendata.fd, fprivdata->file_cluster, fprivdata->gendata.indir_fat_clusters, FAT_VALUE );
			fprivdata->cluster_offset = 0;

		}

	}

	free( cluster_data );

	//  Increase the file position by read
	fprivdata->file_position += data_read;

	PROF_END ( vmc_readProf ) 

	//  Return the amount read in.
	return data_read;

}


//----------------------------------------------------------------------------
// Write a file previously open with fileXioOpen("vmc...
//----------------------------------------------------------------------------
int Vmc_Write (  iop_file_t* f, void* buffer, int size ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: Write %i bytes\n", size );

	if ( g_Vmc_Image[ f->unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ f->unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_writeProf ) 

	if ( ! ( f->mode & O_WRONLY )  ) 
	{

		PROF_END ( vmc_writeProf ) 

		return -1; //  if the file isn't opened for writing, return -1

	}

	struct file_privdata* fprivdata = f->privdata;

	fprivdata->gendata.first_allocatable = g_Vmc_Image[ f->unit ].header.first_allocatable;
	fprivdata->gendata.last_allocatable  = g_Vmc_Image[ f->unit ].header.last_allocatable;

	memcpy ( fprivdata->gendata.indir_fat_clusters, g_Vmc_Image[ f->unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

	if ( f->mode & O_APPEND ) {

		DEBUGPRINT ( 8, "vmcfs: Write with O_APPEND.\n");

		fprivdata->file_position = fprivdata->file_length;

	}

	unsigned int current_cluster;

	//  if we are going to go past the eof with this write
	//  we need to allocate some more free cluster space
	int num_clusters = fprivdata->file_length  /  g_Vmc_Image[ f->unit ].cluster_size;

	if (  ( fprivdata->file_length %g_Vmc_Image[ f->unit ].cluster_size ) > 0 ) 
		num_clusters++;

	DEBUGPRINT ( 5, "vmcfs: File lenght in cluster before this write: %u\n", num_clusters );

	int num_clusters_required = ( fprivdata->file_position + size )  /  g_Vmc_Image[ f->unit ].cluster_size;

	if (  (  ( fprivdata->file_position + size ) %g_Vmc_Image[ f->unit ].cluster_size ) > 0 ) 
		num_clusters_required++;

	DEBUGPRINT ( 5, "vmcfs: File lenght in cluster after this write : %u\n", num_clusters_required );

	if ( num_clusters_required > num_clusters ) 
	{

		fprivdata->file_length = fprivdata->file_position + size;

		DEBUGPRINT ( 3, "vmcfs: File position:  %u\n", fprivdata->file_position );
		DEBUGPRINT ( 3, "vmcfs: Size to write:  %u\n", size );
		DEBUGPRINT ( 3, "vmcfs: File length  :  %u\n", fprivdata->file_length );

		//  now determine how many clusters we need forthis write
		unsigned int clusters_needed = num_clusters_required - num_clusters;

		DEBUGPRINT ( 3, "vmcfs: We need to allocate %u more clusters for storage\n", clusters_needed );

		int i                     = 0;
		unsigned int free_cluster = 0;
		unsigned int last_cluster = 0;

		if ( fprivdata->file_startcluster == EOF_CLUSTER ) 
		{

			free_cluster = getFreeCluster ( &( fprivdata->gendata ), f->unit );

			if ( free_cluster == ERROR_CLUSTER )
			{

				DEBUGPRINT ( 2, "Not enough free space to add initial cluster.  Aborting.\n" );

				PROF_END ( vmc_writeProf ) 

				return -1;

			}

			DEBUGPRINT ( 3, "vmcfs: Initial cluster at %u\n", free_cluster );

			//  mark the free cluster as eof
			setFatEntry ( fprivdata->gendata.fd, free_cluster, EOF_CLUSTER, fprivdata->gendata.indir_fat_clusters, FAT_SET );

			struct direntry dirent;

			readPage ( fprivdata->gendata.fd, ( unsigned char* ) &dirent, fprivdata->file_dirpage );
			dirent.cluster = free_cluster;
			writePage ( fprivdata->gendata.fd, ( unsigned char* ) &dirent, fprivdata->file_dirpage );

			fprivdata->file_startcluster = free_cluster;
			fprivdata->file_cluster      = free_cluster;

		}
		else
		{

			//  loop to find the last cluster of the file.
			last_cluster = fprivdata->file_startcluster;

			DEBUGPRINT ( 3, "vmcfs: Searching last cluster of file\n" );

			while ( 1 ) 
			{

				current_cluster = getFatEntry ( fprivdata->gendata.fd, last_cluster, fprivdata->gendata.indir_fat_clusters, FAT_VALUE );

				if ( current_cluster == FREE_CLUSTER ) 
				{

					DEBUGPRINT ( 10, "vmcfs: Testing cluster %d ... value is FREE_CLUSTER\n", last_cluster );

				}
				else if ( current_cluster == EOF_CLUSTER ) 
				{

					DEBUGPRINT ( 10, "vmcfs: Testing cluster %d ... value is EOF_CLUSTER\n", i );
					break;

				}
				else
				{

					DEBUGPRINT ( 10, "vmcfs: Testing cluster %d ... value is %d\n", last_cluster, current_cluster );

				}

				last_cluster = current_cluster;
				i++;

			}

			DEBUGPRINT ( 3, "vmcfs: Last cluster of file at %u\n", last_cluster );

			free_cluster = getFreeCluster ( &( fprivdata->gendata ), f->unit );

			if ( free_cluster == ERROR_CLUSTER )
			{

				DEBUGPRINT ( 2, "Not enough free space to add restart cluster.  Aborting.\n" );

				PROF_END ( vmc_writeProf ) 

				return -1;

			}
			
			DEBUGPRINT ( 3, "vmcfs: Restart cluster for the file at %u\n", free_cluster );

			//  mark the last cluster as restart of our file
			setFatEntry ( fprivdata->gendata.fd, last_cluster, free_cluster, fprivdata->gendata.indir_fat_clusters, FAT_SET );

			//  mark the free cluster as eof
			setFatEntry ( fprivdata->gendata.fd, free_cluster, EOF_CLUSTER, fprivdata->gendata.indir_fat_clusters, FAT_SET );

			fprivdata->file_cluster = free_cluster;

		}

		clusters_needed--;
		last_cluster = free_cluster;

		//  allocate free clusters for the space required.
		for ( i = 0; i < clusters_needed; i++ ) 
		{

			free_cluster = getFreeCluster ( &( fprivdata->gendata ), f->unit );

			if ( free_cluster == ERROR_CLUSTER )
			{

				DEBUGPRINT ( 2, "Not enough free space to add cluster to the chain.  Aborting.\n" );

				PROF_END ( vmc_writeProf ) 

				return -1;

			}
			
			DEBUGPRINT ( 3, "vmcfs: Adding cluster %u to the chain for the file\n", free_cluster );

			setFatEntry ( fprivdata->gendata.fd, last_cluster, free_cluster, fprivdata->gendata.indir_fat_clusters, FAT_SET );
			last_cluster = free_cluster;

			//  mark the last cluster as eof
			setFatEntry ( fprivdata->gendata.fd, last_cluster, EOF_CLUSTER, fprivdata->gendata.indir_fat_clusters, FAT_SET );

		}

	}

	//  ok space is definitly ready for us to write to...
	current_cluster = fprivdata->file_cluster;

	unsigned int data_written = 0;

	while ( data_written < size ) 
	{

		//  if we have more then a clusters worth of data to write...
		int sizewritten = 0;

		if (  ( size - data_written ) >= ( g_Vmc_Image[ f->unit ].cluster_size - fprivdata->cluster_offset )  ) 
		{

			if ( fprivdata->cluster_offset == 0 ) 
			{

				DEBUGPRINT ( 4, "vmcfs: Writing to cluster %u\n", current_cluster );

				writeCluster ( fprivdata->gendata.fd, (  ( unsigned char* ) buffer ) + data_written, current_cluster + fprivdata->gendata.first_allocatable );
				sizewritten = g_Vmc_Image[ f->unit ].cluster_size;

			}
			else
			{

				DEBUGPRINT ( 4, "vmcfs: Writing to cluster %u at offset %u for length %u\n", current_cluster, fprivdata->cluster_offset, g_Vmc_Image[ f->unit ].cluster_size - fprivdata->cluster_offset );

				writeClusterPart ( fprivdata->gendata.fd, (  ( unsigned char* ) buffer ) + data_written, current_cluster + fprivdata->gendata.first_allocatable, fprivdata->cluster_offset, g_Vmc_Image[ f->unit ].cluster_size - fprivdata->cluster_offset );
				sizewritten = g_Vmc_Image[ f->unit ].cluster_size - fprivdata->cluster_offset;

			}

		}
		else
		{

			DEBUGPRINT ( 4, "vmcfs: Writing to cluster %u at offset %u for length %u\n", current_cluster, fprivdata->cluster_offset, size - data_written );

			writeClusterPart ( fprivdata->gendata.fd, (  ( unsigned char* ) buffer ) + data_written, current_cluster + fprivdata->gendata.first_allocatable, fprivdata->cluster_offset, size - data_written );
			sizewritten = size - data_written;

		}

		DEBUGPRINT ( 5, "vmcfs: Wrote %i bytes\n", sizewritten );

		data_written              += sizewritten;
		fprivdata->file_position  += sizewritten;
		fprivdata->cluster_offset += sizewritten;

		if ( fprivdata->cluster_offset == g_Vmc_Image[ f->unit ].cluster_size ) 
		{

			DEBUGPRINT ( 7, "vmcfs: Moving onto next cluster\n" );

			current_cluster = getFatEntry ( fprivdata->gendata.fd, current_cluster, fprivdata->gendata.indir_fat_clusters, FAT_VALUE );
			fprivdata->cluster_offset = 0;

		}

	}

	struct direntry dirent;

	if ( f->mode & O_TRUNC || f->mode & O_APPEND )
	{

		DEBUGPRINT ( 8, "vmcfs: Write with O_TRUNC.\n");

		fprivdata->file_length = fprivdata->file_position;

	}

	readPage ( fprivdata->gendata.fd, ( unsigned char* ) &dirent, fprivdata->file_dirpage );

	// Update file length
	dirent.length = fprivdata->file_length;

	// Update timestamp
	getPs2Time ( &dirent.modified );

	// Write this changes
	writePage ( fprivdata->gendata.fd, ( unsigned char* ) &dirent, fprivdata->file_dirpage );

	fprivdata->file_cluster = current_cluster;

	PROF_END ( vmc_writeProf ) 

	return data_written;

}


//----------------------------------------------------------------------------
// Seek a file previously open with fileXioOpen("vmc...
//----------------------------------------------------------------------------
int Vmc_Lseek ( iop_file_t* f, unsigned long offset, int whence ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: Seek\n" );

	if ( g_Vmc_Image[ f->unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ f->unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_lseekProf ) 

	struct file_privdata* fprivdata = f->privdata;

	switch ( whence ) {

		case SEEK_SET:
			fprivdata->file_position = offset;
			break;

		case SEEK_CUR:
			fprivdata->file_position += offset;
			break;

		case SEEK_END:
			fprivdata->file_position = fprivdata->file_length + offset;
			break;

		default:
			return -1;

	}

	//  Return an error if we are past the end of the file
	if ( fprivdata->file_position > fprivdata->file_length ) 
		return -1;

	//  Now we need to position cluster offsets to be correct
	fprivdata->file_cluster = fprivdata->file_startcluster;

	int i = 0;
	int chainclusternum = fprivdata->file_position  /  g_Vmc_Image[ f->unit ].cluster_size;

	for ( i = 0; i < chainclusternum; i++ ) 
		fprivdata->file_cluster = getFatEntry ( fprivdata->gendata.fd, fprivdata->file_cluster, fprivdata->gendata.indir_fat_clusters, FAT_VALUE );

	fprivdata->cluster_offset = fprivdata->file_position %g_Vmc_Image[ f->unit ].cluster_size;

	PROF_END ( vmc_lseekProf ) 

	return fprivdata->file_position;

}


//----------------------------------------------------------------------------
// Control command.
// IOCTL_VMCFS_RECOVER :  Recover an object marked as none existing. ( data must be a valid path to an object in vmc file ) 
//----------------------------------------------------------------------------
int Vmc_Ioctl ( iop_file_t* f, unsigned long request, void* data ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	switch ( request ) 
	{

		case IOCTL_VMCFS_RECOVER:
			{

				Vmc_Recover ( f->unit, ( char* ) data );

			}
			break;

		default:

			DEBUGPRINT ( 1, "vmcfs: Unrecognized ioctl command %ld\n", request );
			break;

	}

	return VMCFS_ERR_NO;

}


//----------------------------------------------------------------------------
// Remove an object from vmc image. Object can be a file or a folder.
//----------------------------------------------------------------------------
int Vmc_Remove ( iop_file_t* f, const char* path ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: remove %s\n", path );

	if ( g_Vmc_Image[ f->unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ f->unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_removeProf ) 

	struct direntry     dirent;
	struct gen_privdata gendata;

	gendata.fd                = g_Vmc_Image[ f->unit ].fd;
	gendata.first_allocatable = g_Vmc_Image[ f->unit ].header.first_allocatable;
	gendata.last_allocatable  = g_Vmc_Image[ f->unit ].header.last_allocatable;

	memcpy ( gendata.indir_fat_clusters, g_Vmc_Image[ f->unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

	unsigned int dirent_cluster = getDirentryFromPath ( &dirent, path, &gendata, f->unit );

	if ( dirent_cluster == ROOT_CLUSTER )
	{

		DEBUGPRINT ( 2, "vmcfs: remove failed. Root directory is protected.\n" );

		PROF_END ( vmc_removeProf ) 

		return -1;

	}
	else if ( dirent_cluster == NOFOUND_CLUSTER )
	{

		DEBUGPRINT ( 2, "vmcfs: remove failed. %s not found.\n", path );

		PROF_END ( vmc_removeProf ) 

		return -1;

	}

	if ( dirent.mode & DF_FILE ) 
	{

		removeObject ( &gendata, dirent_cluster, &dirent, f->unit );

		DEBUGPRINT ( 2, "vmcfs: file %s removed.\n", path );

	}
	else
	{

		DEBUGPRINT ( 2, "vmcfs: remove failed. %s is not a valid file.\n", path );

		PROF_END ( vmc_removeProf ) 

		return -1;

	}

	//  ioman Bug Fix. mkdir is call after remove fonction
	g_Vmc_Remove_Flag = TRUE;
	
	PROF_END ( vmc_removeProf ) 

	return 0;

}


//----------------------------------------------------------------------------
// Create a new folder into vmc image.
//----------------------------------------------------------------------------
int Vmc_Mkdir ( iop_file_t* f, const char* path1, int mode ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	//  ioman bug fix. mkdir is call after remove fonction
	if ( g_Vmc_Remove_Flag == TRUE ) 
	{

		g_Vmc_Remove_Flag = FALSE;
		printf ( "vmcfs: mkdir stopped. Ioman bug fix.\n" );
		return 0;

	} 

	DEBUGPRINT ( 1, "vmcfs: mkdir %s\n", path1 );

	if ( g_Vmc_Image[ f->unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ f->unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_mkdirProf ) 

	struct direntry     dirent;
	struct gen_privdata gendata;

	gendata.fd                = g_Vmc_Image[ f->unit ].fd;
	gendata.first_allocatable = g_Vmc_Image[ f->unit ].header.first_allocatable;
	gendata.last_allocatable  = g_Vmc_Image[ f->unit ].header.last_allocatable;

	memcpy ( gendata.indir_fat_clusters, g_Vmc_Image[ f->unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

	memset ( &dirent, 0, sizeof ( dirent )  );

	// Make a local copy of path.
	char* path = malloc ( strlen ( path1 ) + 1 );
	memcpy ( path, path1, strlen ( path1 ) + 1 ); 

	if ( path[ strlen ( path ) - 1 ] == '/' ) 
		path[ strlen ( path ) - 1 ]= '\0';

	unsigned int dirent_cluster = getDirentryFromPath ( &dirent, path, &gendata, f->unit );

	if ( dirent_cluster == ROOT_CLUSTER )
	{

		DEBUGPRINT ( 2, "vmcfs: mkdir failed. Root directory is protected.\n" );
			
		free( path );

		PROF_END ( vmc_mkdirProf ) 

		return -1;	//  File not found, could not be opened.

	}
	else if ( dirent_cluster == NOFOUND_CLUSTER )
	{

		struct direntry     new_dirent;
		unsigned int        new_dirent_cluster = 0;
		struct direntry     parent_dirent;
		unsigned int        parent_cluster = 0;
		struct gen_privdata parent_gendata;

		parent_gendata.fd                = g_Vmc_Image[ f->unit ].fd;
		parent_gendata.first_allocatable = g_Vmc_Image[ f->unit ].header.first_allocatable;
		parent_gendata.last_allocatable  = g_Vmc_Image[ f->unit ].header.last_allocatable;

		memcpy ( parent_gendata.indir_fat_clusters, g_Vmc_Image[ f->unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

		// Find name of directory, and name of parent
		char* newdir = strrchr ( path, '/' );

		newdir[ 0 ] = '\0';
		newdir++;

		DEBUGPRINT ( 2, "vmcfs: Creating folder %s in parent folder %s\n", newdir, (path[0] == '\0') ? "Root" : path );

		memset ( &new_dirent, 0, sizeof ( new_dirent )  );

		//  fill new direntry
		new_dirent.mode = DF_EXISTS | DF_0400 | DF_DIRECTORY | DF_READ | DF_WRITE | DF_EXECUTE; //  0x8427
		strcpy ( new_dirent.name, newdir );
		getPs2Time ( &new_dirent.created );
		getPs2Time ( &new_dirent.modified );

		if ( path[ 0 ] == '\0' ) 
		{

			//  get the root directories entry
			parent_cluster = getDirentryFromPath ( &parent_dirent, ".", &parent_gendata, f->unit );

		}
		else
		{

			//  get the folder entry for the parent
			parent_cluster = getDirentryFromPath ( &parent_dirent, path, &parent_gendata, f->unit );

		}

		if ( parent_cluster == NOFOUND_CLUSTER )
		{

			DEBUGPRINT ( 3, "vmcfs: Unable to find parent directory\n" );

			free ( path );

			PROF_END ( vmc_mkdirProf ) 

			return -1;

		}

		DEBUGPRINT ( 3, "vmcfs: Parent Information.\n" );
		DEBUGPRINT ( 3, "vmcfs: parent_cluster  = %u\n", parent_cluster );
		DEBUGPRINT ( 3, "vmcfs: dir_cluster    = %u\n", parent_dirent.cluster );
		DEBUGPRINT ( 3, "vmcfs: dirent.name    = %s\n", parent_dirent.name );
		DEBUGPRINT ( 3, "vmcfs: dirent.length  = %u\n", parent_dirent.length );
		DEBUGPRINT ( 3, "vmcfs: dirent.mode    = %X\n", parent_dirent.mode );
		DEBUGPRINT ( 3, "vmcfs: dirent_page    = %i\n", parent_gendata.dirent_page );

		new_dirent_cluster = addObject ( &parent_gendata, parent_cluster, &parent_dirent, &new_dirent, f->unit );

		if ( new_dirent_cluster == ERROR_CLUSTER )
		{

			DEBUGPRINT ( 2, "vmcfs: mkdir failed on %s\n", path );

			free ( path );

			PROF_END ( vmc_mkdirProf ) 

			return -1;

		}

	}
	else // directory allready exist, so test DF_EXISTS flag
	{

		if ( dirent.mode & DF_EXISTS ) 
		{

			DEBUGPRINT ( 2, "vmcfs: mkdir failed on %s. Directory allready exist.\n", path1 );

			free( path );

			PROF_END ( vmc_mkdirProf ) 

			return -1;

		}
		else
		{

			DEBUGPRINT ( 8, "vmcfs: mkdir directory %s allready exist but is hidden. Changing attributs.\n", path1 );

			DEBUGPRINT ( 8, "vmcfs: Following fat table cluster %u\n", dirent.cluster );

			unsigned int pseudo_entry_cluster = getFatEntry ( gendata.fd, dirent.cluster, gendata.indir_fat_clusters, FAT_VALUE );

			DEBUGPRINT ( 8, "vmcfs: Changing cluster mask of fat table cluster %u.\n", pseudo_entry_cluster );

			// change cluster mask of the direntry
			setFatEntry ( gendata.fd, dirent.cluster, pseudo_entry_cluster, gendata.indir_fat_clusters, FAT_SET );

			DEBUGPRINT ( 8, "vmcfs: Changing direntry %s attributs.\n", path1 );

			//  Update time stamp, and set dirent.mode to exist flag
			dirent.mode	= dirent.mode | DF_EXISTS;
			getPs2Time ( &dirent.created );
			getPs2Time ( &dirent.modified );
			writePage ( gendata.fd, ( unsigned char* ) &dirent, ( dirent_cluster + gendata.first_allocatable ) * g_Vmc_Image[ f->unit ].header.pages_per_cluster + gendata.dirent_page );

			DEBUGPRINT ( 8, "vmcfs: Restoring EOF cluster at %u.\n", pseudo_entry_cluster );

			setFatEntry ( gendata.fd, pseudo_entry_cluster, EOF_CLUSTER, gendata.indir_fat_clusters, FAT_SET );

			struct direntry pseudo_entries;

			DEBUGPRINT ( 8, "vmcfs: Updating pseudo entries time stamps at cluster %u / page %u.\n", dirent.cluster + gendata.first_allocatable, ( dirent.cluster + gendata.first_allocatable ) * g_Vmc_Image[ f->unit ].header.pages_per_cluster );

			//  Update time stamp of '.' entry
			readPage ( gendata.fd, ( unsigned char* ) &pseudo_entries, ( dirent.cluster + gendata.first_allocatable ) * g_Vmc_Image[ f->unit ].header.pages_per_cluster );
			getPs2Time ( &pseudo_entries.created );
			getPs2Time ( &pseudo_entries.modified );
			writePage ( gendata.fd, ( unsigned char* ) &pseudo_entries, ( dirent.cluster + gendata.first_allocatable ) * g_Vmc_Image[ f->unit ].header.pages_per_cluster );

			DEBUGPRINT ( 8, "vmcfs: Updating pseudo entries time stamps at cluster %u / page %u.\n", dirent.cluster + gendata.first_allocatable, ( dirent.cluster + gendata.first_allocatable ) * g_Vmc_Image[ f->unit ].header.pages_per_cluster + 1 );

			//  Update time stamp of '..' entry
			readPage ( gendata.fd, ( unsigned char* ) &pseudo_entries, ( dirent.cluster + gendata.first_allocatable ) * g_Vmc_Image[ f->unit ].header.pages_per_cluster + 1 );
			getPs2Time ( &pseudo_entries.created );
			getPs2Time ( &pseudo_entries.modified );
			writePage ( gendata.fd, ( unsigned char* ) &pseudo_entries, ( dirent.cluster + gendata.first_allocatable ) * g_Vmc_Image[ f->unit ].header.pages_per_cluster + 1 );

			free( path );

		}

	}
	
	DEBUGPRINT ( 3, "vmcfs: Directory %s created.\n", path1 );

	PROF_END ( vmc_mkdirProf ) 

	return 0;

}


//----------------------------------------------------------------------------
// Remove a folder from vmc image.
//----------------------------------------------------------------------------
int Vmc_Rmdir ( iop_file_t* f, const char* path1 ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: rmdir %s\n", path1 );

	if ( g_Vmc_Image[ f->unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ f->unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_rmdirProf ) 

	struct direntry     dirent;
	struct gen_privdata folder_gendata;

	folder_gendata.fd                = g_Vmc_Image[ f->unit ].fd;
	folder_gendata.first_allocatable = g_Vmc_Image[ f->unit ].header.first_allocatable;
	folder_gendata.last_allocatable  = g_Vmc_Image[ f->unit ].header.last_allocatable;

	memcpy ( folder_gendata.indir_fat_clusters, g_Vmc_Image[ f->unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

	// Make a local copy of path
	char* path = malloc ( strlen ( path1 ) + 1 );
	memcpy ( path, path1, strlen ( path1 ) + 1 );

	if ( path[ strlen ( path ) - 1 ] == '/' ) 
		path[ strlen ( path ) - 1 ]= '\0';

	unsigned int dirent_cluster = getDirentryFromPath ( &dirent, path, &folder_gendata, f->unit );

	if ( dirent_cluster == ROOT_CLUSTER )
	{

		DEBUGPRINT ( 2, "vmcfs: rmdir failed. Root directory is protected.\n" );

		free( path );

		PROF_END ( vmc_rmdirProf ) 

		return -1;

	}
	else if ( dirent_cluster == NOFOUND_CLUSTER )
	{

		DEBUGPRINT ( 2, "vmcfs: rmdir failed. %s not found.\n", path1 );

		free( path );

		PROF_END ( vmc_rmdirProf ) 

		return -1;

	}

	if ( ! ( dirent.mode & DF_EXISTS ) )
	{

		DEBUGPRINT ( 2, "vmcfs: rmdir failed. %s is allready removed.\n", path1 );

		free( path );

		PROF_END ( vmc_rmdirProf ) 

		return -1;


	}

	if ( dirent.mode & DF_DIRECTORY ) 
	{

		// Find name of directory, and name of parent
		char* foldername = strrchr ( path, '/' );

		foldername[ 0 ]= '\0';
		foldername++;

		struct direntry     child;
		unsigned int        child_cluster;
		struct gen_privdata child_gendata;
		char*               child_path;
		
		child_path = ( char* )malloc ( MAX_PATH );
		memset ( child_path, 0, MAX_PATH );

		int                 i              = 0;
		int                 dir_number     = 0;
		unsigned int        search_cluster = dirent.cluster;

		child_gendata.fd                = g_Vmc_Image[ f->unit ].fd;
		child_gendata.first_allocatable = g_Vmc_Image[ f->unit ].header.first_allocatable;
		child_gendata.last_allocatable  = g_Vmc_Image[ f->unit ].header.last_allocatable;

		memcpy ( child_gendata.indir_fat_clusters, g_Vmc_Image[ f->unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

		// remove file contained into the directory
		for ( i = 0; i < dirent.length; i++ )
		{

			// read in the next directory entry
			readPage ( folder_gendata.fd, ( unsigned char* ) &child, ( search_cluster + folder_gendata.first_allocatable ) * g_Vmc_Image[ f->unit ].header.pages_per_cluster + dir_number );
			
			// if child exist, remove it. But don't touch to pseudo entries.
			if ( child.mode & DF_EXISTS && i >= 2 )
			{

				sprintf( child_path, "%s/%s/%s", path, dirent.name, child.name );

				child_cluster = getDirentryFromPath ( &child, child_path, &child_gendata, f->unit );

				if ( child_cluster != NOFOUND_CLUSTER )
				{

					removeObject ( &child_gendata, child_cluster, &child, f->unit );

				}

			}

			if ( dir_number == 1 ) 
			{

				dir_number  = 0;
				search_cluster = getFatEntry ( folder_gendata.fd, search_cluster, folder_gendata.indir_fat_clusters, FAT_VALUE );

			}
			else
			{

				dir_number = 1;

			}

		}

		// finaly, remove directory
		removeObject ( &folder_gendata, dirent_cluster, &dirent, f->unit );

		free( path );
		free( child_path );

	}
	else
	{

		DEBUGPRINT ( 2, "vmcfs: rmdir failed. %s is not a valid directory.\n", path1 );

		free( path );

		PROF_END ( vmc_rmdirProf ) 

		return -1;

	}

	DEBUGPRINT ( 3, "vmcfs: Directory %s removed.\n", path1 );

	//  ioman Bug Fix. mkdir is call after remove fonction
	g_Vmc_Remove_Flag = TRUE;
	
	PROF_END ( vmc_removeProf ) 

	return 0;

}


//----------------------------------------------------------------------------
// Open a folder in vmc image.
//----------------------------------------------------------------------------
int Vmc_Dopen (  iop_file_t* f, const char* path ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: dopen %i %s\n", f->unit, path );

	if ( g_Vmc_Image[ f->unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ f->unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_dopenProf ) 

	struct direntry      dirent;
	struct dir_privdata* fprivdata = malloc ( sizeof ( struct dir_privdata ) );

	if ( fprivdata == NULL )
		return -1;

	f->privdata = fprivdata;

	fprivdata->gendata.fd                = g_Vmc_Image[ f->unit ].fd;
	fprivdata->gendata.first_allocatable = g_Vmc_Image[ f->unit ].header.first_allocatable;

	memcpy ( fprivdata->gendata.indir_fat_clusters, g_Vmc_Image[ f->unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

	unsigned int dirent_cluster = getDirentryFromPath ( &dirent, path, &( fprivdata->gendata ), f->unit );

	if ( dirent_cluster == NOFOUND_CLUSTER )
	{

		DEBUGPRINT ( 2, "vmcfs: dopen failed. %s not found.\n", path );

		free ( fprivdata ); //  Release the allocated memory

		PROF_END ( vmc_dopenProf ) 

		return -1; //  Folder not found, could not be opened.

	}

	if ( ! ( dirent.mode & DF_EXISTS ) )
	{

		DEBUGPRINT ( 2, "vmcfs: dopen failed. %s is hidden.\n", path );

		free ( fprivdata ); //  Release the allocated memory

		PROF_END ( vmc_dopenProf ) 

		return -1; //  Folder not found, could not be opened.

	}

	fprivdata->dir_cluster = dirent.cluster;
	fprivdata->dir_length  = dirent.length;
	fprivdata->dir_number  = 0;

	DEBUGPRINT ( 2, "vmcfs: Directory %s opened with length %u\n", path, fprivdata->dir_length );

	PROF_END ( vmc_dopenProf ) 

	return 1;

}


//----------------------------------------------------------------------------
// Close a folder in vmc image.
//----------------------------------------------------------------------------
int Vmc_Dclose (  iop_file_t* f ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: dclose %i\n", f->unit );

	if (  ( f->unit == 2 ) || ( f->unit == 3 )  ) 
		return 0; //  Close our fake device used only for ioctl commands

	PROF_START ( vmc_dcloseProf ) 

	struct dir_privdata * fprivdata = f->privdata;

	free ( fprivdata );

	PROF_END ( vmc_dcloseProf ) 

	return 0;

}


//----------------------------------------------------------------------------
// Read the content of a folder in vmc image.
//----------------------------------------------------------------------------
int Vmc_Dread (  iop_file_t* f, iox_dirent_t *buffer ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: dread %i\n", f->unit );

	if ( g_Vmc_Image[ f->unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ f->unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_dreadProf ) 

	int                  next;
	vmc_datetime         access_time;
	struct direntry      dirent;
	struct dir_privdata * fprivdata = f->privdata;
	iox_dirent_t        * buf       = buffer;

	memset ( buf, 0, sizeof ( iox_dirent_t )  );

next_entry:

	next = 0;

	if ( fprivdata->dir_length == 0 ) 
		return -1;

	//  read in the next directory entry
	readPage ( fprivdata->gendata.fd, ( unsigned char* ) &dirent, ( fprivdata->dir_cluster + fprivdata->gendata.first_allocatable ) * g_Vmc_Image[ f->unit ].header.pages_per_cluster + fprivdata->dir_number );

	DEBUGPRINT ( 4, "vmcfs: name: %s; cluster %u; length %u; mode 0x%02x\n", dirent.name, dirent.cluster, dirent.length, dirent.mode );

	if ( dirent.mode & DF_EXISTS ) 
	{
	
		buf->stat.mode = dirent.mode;
		buf->stat.attr = dirent.attr;
		buf->stat.size = dirent.length;

		//  File created Time  /  Date
		memcpy ( buf->stat.ctime, &dirent.created, sizeof ( vmc_datetime )  );

		//  File Modification Time  /  Date
		memcpy ( buf->stat.mtime, &dirent.modified, sizeof ( vmc_datetime )  );

		//  Last File Access Time : now
		getPs2Time ( &access_time );
		memcpy ( buf->stat.atime, &access_time, sizeof ( vmc_datetime )  );

		buf->stat.hisize = 0; //  No idea what hisize is?

		strcpy ( buf->name, dirent.name );

	}
	else
	{

		next = 1;

	}

	fprivdata->dir_length--;
	//  return 1 if there are more entries to read, otherwise return -1

	if ( fprivdata->dir_number ) 
	{

		fprivdata->dir_number  = 0;
		fprivdata->dir_cluster = getFatEntry ( fprivdata->gendata.fd, fprivdata->dir_cluster, fprivdata->gendata.indir_fat_clusters, FAT_VALUE );

	}
	else
	{

		fprivdata->dir_number = 1;

	}
	
	if ( next == 1 ) 
		goto next_entry;

	PROF_END ( vmc_dreadProf ) 

	return 1;

}


//----------------------------------------------------------------------------
// Get stats from an object in vmc image.
//----------------------------------------------------------------------------
int Vmc_Getstat ( iop_file_t* f, const char* path, iox_stat_t * stat ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: getstat %i %s\n", f->unit, path );

	if ( g_Vmc_Image[ f->unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ f->unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_getstatProf ) 

	struct direntry     dirent;
	struct gen_privdata gendata;      
	vmc_datetime        access_time;

	gendata.fd                = g_Vmc_Image[ f->unit ].fd;
	gendata.first_allocatable = g_Vmc_Image[ f->unit ].header.first_allocatable;
	gendata.last_allocatable  = g_Vmc_Image[ f->unit ].header.last_allocatable;

	memcpy ( gendata.indir_fat_clusters, g_Vmc_Image[ f->unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

	unsigned int dirent_cluster = getDirentryFromPath ( &dirent, path, &gendata, f->unit );

	if ( dirent_cluster == NOFOUND_CLUSTER )
	{

		DEBUGPRINT ( 2, "vmcfs: getstat failed. %s not found.\n", path );

		PROF_END ( vmc_getstatProf ) 

		return -1;

	}

	stat->mode = dirent.mode;
	stat->attr = dirent.attr;
	stat->size = dirent.length;

	//  File created Time  /  Date
	memcpy ( stat->ctime, &dirent.created, sizeof ( vmc_datetime )  );

	//  File Modification Time  /  Date
	memcpy ( stat->mtime, &dirent.modified, sizeof ( vmc_datetime )  );

	//  Last File Access Time : now
	getPs2Time ( &access_time );
	memcpy ( stat->atime, &access_time, sizeof ( vmc_datetime )  );

	stat->hisize = 0; //  No idea what hisize is?

	PROF_END ( vmc_getstatProf ) 

	return 0;

}


//----------------------------------------------------------------------------
// Put stats to an object in vmc image.
//----------------------------------------------------------------------------
int Vmc_Chstat ( iop_file_t* f, const char* path, iox_stat_t * stat, unsigned int unknown ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: chstat %i %s\n", f->unit, path );

	if ( g_Vmc_Image[ f->unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ f->unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_chstatProf ) 

	struct direntry     dirent;
	struct gen_privdata gendata;

	gendata.fd                = g_Vmc_Image[ f->unit ].fd;
	gendata.first_allocatable = g_Vmc_Image[ f->unit ].header.first_allocatable;
	gendata.last_allocatable  = g_Vmc_Image[ f->unit ].header.last_allocatable;

	memcpy ( gendata.indir_fat_clusters, g_Vmc_Image[ f->unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

	unsigned int dirent_cluster = getDirentryFromPath ( &dirent, path, &gendata, f->unit );

	if ( dirent_cluster == NOFOUND_CLUSTER )
	{

		DEBUGPRINT ( 2, "vmcfs: chstat failed. %s not found.\n", path );

		PROF_END ( vmc_chstatProf ) 

		return -1;

	}

	dirent.mode	  = stat->mode;
	dirent.attr	  = stat->attr;
	dirent.length	 = stat->size;

	//  File created Time  /  Date
	memcpy ( &dirent.created, stat->ctime, sizeof ( vmc_datetime )  );

	//  File Modification Time  /  Date
	memcpy ( &dirent.created, stat->mtime, sizeof ( vmc_datetime )  );

	//  Write this change
	writePage ( gendata.fd, ( unsigned char* ) &dirent, ( dirent_cluster + gendata.first_allocatable ) * g_Vmc_Image[ f->unit ].header.pages_per_cluster + gendata.dirent_page );

	PROF_END ( vmc_chstatProf ) 

	return 0;

}


// Start of extended io fonctions.


//----------------------------------------------------------------------------
// Rename an object into a vmc file.
//----------------------------------------------------------------------------
int Vmc_Rename ( iop_file_t* f, const char* path, const char* new_name )
{
	
	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: rename %s\n", path );

	if ( g_Vmc_Image[ f->unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ f->unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_renameProf ) 

	struct direntry     dirent;
	struct gen_privdata gendata;

	gendata.fd                = g_Vmc_Image[ f->unit ].fd;
	gendata.first_allocatable = g_Vmc_Image[ f->unit ].header.first_allocatable;
	gendata.last_allocatable  = g_Vmc_Image[ f->unit ].header.last_allocatable;

	memcpy ( gendata.indir_fat_clusters, g_Vmc_Image[ f->unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

	unsigned int dirent_cluster = getDirentryFromPath ( &dirent, path, &gendata, f->unit );

	if ( dirent_cluster == ROOT_CLUSTER )
	{

		DEBUGPRINT ( 2, "vmcfs: rename failed. Root directory is protected.\n" );

		PROF_END ( vmc_renameProf ) 

		return -1;

	}
	else if ( dirent_cluster == NOFOUND_CLUSTER )
	{

		DEBUGPRINT ( 2, "vmcfs: rename failed. %s not found.\n", path );

		PROF_END ( vmc_renameProf ) 

		return -1;

	}

	//  Change the name of the object
	strcpy ( dirent.name, new_name );

	// Update timestamp
	getPs2Time ( &dirent.modified );

	//  Write this change
	writePage ( gendata.fd, ( unsigned char* ) &dirent, ( dirent_cluster * g_Vmc_Image[ f->unit ].header.pages_per_cluster ) + gendata.dirent_page );

	DEBUGPRINT ( 2, "vmcfs: Object %s renamed to %s\n", path, new_name );

	PROF_END ( vmc_renameProf ) 

	return 0;
	
}


//----------------------------------------------------------------------------
// Not Implemented for the moment.
//----------------------------------------------------------------------------
int Vmc_Chdir ( iop_file_t* f, const char* path )
{
	
	return VMCFS_ERR_IMPLEMENTED;
	
}


//----------------------------------------------------------------------------
// Not Implemented for the moment.
//----------------------------------------------------------------------------
int Vmc_Sync ( iop_file_t* f, const char* device, int flag )
{
	
	return VMCFS_ERR_IMPLEMENTED;

}


//----------------------------------------------------------------------------
// Mount a vmc file with fileXioMount(... call.
//----------------------------------------------------------------------------
int Vmc_Mount ( iop_file_t* f, const char* fsname, const char* devname, int flag, void *arg, unsigned int arg_len )
{
	int errcode;

	DEBUGPRINT ( 2, "vmcfs: Mount %s at mount point: %d\n", devname, f->unit );

	if ( g_Vmc_Image[ f->unit ].fd >= 0 )
		return VMCFS_ERR_MOUNT_BUSY;

	g_Vmc_Image[ f->unit ].fd = open ( devname, O_RDWR, 0x666 );

	if ( g_Vmc_Image[ f->unit ].fd < 0 )
	{
			DEBUGPRINT ( 1, "vmcfs: Error opening vmc file %s\n", devname );
			DEBUGPRINT ( 1, "vmcfs: open error code: %d\n", g_Vmc_Image[ f->unit ].fd );

			return VMCFS_ERR_VMC_OPEN;
	}

	//  read informations from the superblock
	int r = read ( g_Vmc_Image[ f->unit ].fd, &g_Vmc_Image[ f->unit ].header, sizeof ( struct superblock )  );

	if ( r != sizeof ( struct superblock )  )
	{
		DEBUGPRINT ( 1, "vmcfs: Error reading vmc file %s\n", devname );
		DEBUGPRINT ( 1, "vmcfs: fd: %d, error code:%d\n", g_Vmc_Image[ f->unit ].fd, r );

		errcode = VMCFS_ERR_VMC_READ;

mountAbort:
		close ( g_Vmc_Image[ f->unit ].fd );
		g_Vmc_Image[ f->unit ].fd = VMCFS_ERR_NOT_MOUNT;
		return errcode;
	}

	g_Vmc_Image[ f->unit ].card_size = lseek ( g_Vmc_Image[ f->unit ].fd, 0, SEEK_END );
	lseek ( g_Vmc_Image[ f->unit ].fd, 0, SEEK_SET );

	if(		g_Vmc_Image[ f->unit ].header.magic[  0 ] != 'S'
		||	g_Vmc_Image[ f->unit ].header.magic[  1 ] != 'o'
		||	g_Vmc_Image[ f->unit ].header.magic[  2 ] != 'n'
		||	g_Vmc_Image[ f->unit ].header.magic[  3 ] != 'y'
		||	g_Vmc_Image[ f->unit ].header.magic[  4 ] != ' '
		||	g_Vmc_Image[ f->unit ].header.magic[  5 ] != 'P'
		||	g_Vmc_Image[ f->unit ].header.magic[  6 ] != 'S'
		||	g_Vmc_Image[ f->unit ].header.magic[  7 ] != '2'
		||	g_Vmc_Image[ f->unit ].header.magic[  8 ] != ' '
		||	g_Vmc_Image[ f->unit ].header.magic[  9 ] != 'M'
		||	g_Vmc_Image[ f->unit ].header.magic[ 10 ] != 'e'
		||	g_Vmc_Image[ f->unit ].header.magic[ 11 ] != 'm'
		||	g_Vmc_Image[ f->unit ].header.magic[ 12 ] != 'o'
		||	g_Vmc_Image[ f->unit ].header.magic[ 13 ] != 'r'
		||	g_Vmc_Image[ f->unit ].header.magic[ 14 ] != 'y'
		||	g_Vmc_Image[ f->unit ].header.magic[ 15 ] != ' '
		||	g_Vmc_Image[ f->unit ].header.magic[ 16 ] != 'C'
		||	g_Vmc_Image[ f->unit ].header.magic[ 17 ] != 'a'
		||	g_Vmc_Image[ f->unit ].header.magic[ 18 ] != 'r'
		||	g_Vmc_Image[ f->unit ].header.magic[ 19 ] != 'd'
		||	g_Vmc_Image[ f->unit ].header.magic[ 20 ] != ' '
		||	g_Vmc_Image[ f->unit ].header.magic[ 21 ] != 'F'
		||	g_Vmc_Image[ f->unit ].header.magic[ 22 ] != 'o'
		||	g_Vmc_Image[ f->unit ].header.magic[ 23 ] != 'r'
		||	g_Vmc_Image[ f->unit ].header.magic[ 24 ] != 'm'
		||	g_Vmc_Image[ f->unit ].header.magic[ 25 ] != 'a'
		||	g_Vmc_Image[ f->unit ].header.magic[ 26 ] != 't'
		)
	{
		//  Card is not formated
		DEBUGPRINT ( 1, "vmcfs: Warning vmc file %s is not formated\n", devname );
		if ( !setDefaultSpec ( f->unit )  ) 
		{
			//  Card size error
			DEBUGPRINT ( 1, "vmcfs: Error size of vmc file %s is incompatible\n", devname );

			errcode = VMCFS_ERR_VMC_SIZE;
			goto mountAbort;
		}
		g_Vmc_Image[ f->unit ].formated = FALSE;
	}else{
		g_Vmc_Image[ f->unit ].formated = TRUE;

		DEBUGPRINT ( 4, "vmcfs: SuperBlock readed from vmc file %s.\n", devname );

		DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: magic[40]             : %s\n"   , g_Vmc_Image[ f->unit ].header.magic                  );
		DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: page_size             : 0x%02x\n", g_Vmc_Image[ f->unit ].header.page_size              );
		DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: pages_per_cluster     : 0x%02x\n", g_Vmc_Image[ f->unit ].header.pages_per_cluster      );
		DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: pages_per_block       : 0x%02x\n", g_Vmc_Image[ f->unit ].header.pages_per_block        );
		DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: clusters_per_card     : 0x%02x\n", g_Vmc_Image[ f->unit ].header.clusters_per_card      );
		DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: first_allocatable     : 0x%02x\n", g_Vmc_Image[ f->unit ].header.first_allocatable      );
		DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: last_allocatable      : 0x%02x\n", g_Vmc_Image[ f->unit ].header.last_allocatable       );
		DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: root_cluster          : 0x%02x\n", g_Vmc_Image[ f->unit ].header.root_cluster           );
		DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: backup_block1         : 0x%02x\n", g_Vmc_Image[ f->unit ].header.backup_block1          );
		DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: backup_block2         : 0x%02x\n", g_Vmc_Image[ f->unit ].header.backup_block2          );
		for ( r = 0; g_Vmc_Image[ f->unit ].header.indir_fat_clusters[ r ]!= 0; r++ ) 
			DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: indir_fat_clusters[%d] : 0x%02x\n", r, g_Vmc_Image[ f->unit ].header.indir_fat_clusters[ r ]);
		DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: mc_type               : 0x%02x\n", g_Vmc_Image[ f->unit ].header.mc_type                );
		DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: mc_flag               : 0x%02x\n", g_Vmc_Image[ f->unit ].header.mc_flag                );

		if ( g_Vmc_Image[ f->unit ].header.mc_type != PS2_MEMORYCARD )
		{
			//  Card is not a PS2 one
			DEBUGPRINT ( 1, "vmcfs: Error vmc file %s is not a valid PS2 image\n", devname );

			errcode = VMCFS_ERR_CARD_TYPE;
			goto mountAbort;
		}

		//Reaching this point means we have a valid PS2 image
		DEBUGPRINT ( 4, "vmcfs: Image file Info: Vmc card type         : %s MemoryCard.\n", ( g_Vmc_Image[ f->unit ].header.mc_type == PSX_MEMORYCARD ? "PSX" : ( g_Vmc_Image[ f->unit ].header.mc_type == PS2_MEMORYCARD ? "PS2" : "PDA" )  )  );

		g_Vmc_Image[ f->unit ].total_pages       = g_Vmc_Image[ f->unit ].header.pages_per_cluster * g_Vmc_Image[ f->unit ].header.clusters_per_card;
		g_Vmc_Image[ f->unit ].cluster_size      = g_Vmc_Image[ f->unit ].header.page_size * g_Vmc_Image[ f->unit ].header.pages_per_cluster;
		g_Vmc_Image[ f->unit ].erase_byte        = ( g_Vmc_Image[ f->unit ].header.mc_flag & 0x10 ) ? 0x0 : 0xFF;
		g_Vmc_Image[ f->unit ].last_idc          = EOF_CLUSTER;
		g_Vmc_Image[ f->unit ].last_cluster      = EOF_CLUSTER;
		g_Vmc_Image[ f->unit ].last_free_cluster = g_Vmc_Image[ f->unit ].header.first_allocatable;

		memset ( &g_Vmc_Image[ f->unit ].indirect_cluster, g_Vmc_Image[ f->unit ].erase_byte, MAX_CLUSTER_SIZE );
		memset ( &g_Vmc_Image[ f->unit ].fat_cluster, g_Vmc_Image[ f->unit ].erase_byte, MAX_CLUSTER_SIZE );

		if ( g_Vmc_Image[ f->unit ].card_size == (  ( g_Vmc_Image[ f->unit ].header.page_size + 0x10 ) * g_Vmc_Image[ f->unit ].total_pages )  ) 
		{
			g_Vmc_Image[ f->unit ].ecc_flag = TRUE;
		}
		else if ( g_Vmc_Image[ f->unit ].card_size == ( g_Vmc_Image[ f->unit ].header.page_size * g_Vmc_Image[ f->unit ].total_pages )  ) 
		{
			g_Vmc_Image[ f->unit ].ecc_flag = FALSE;
		}
		else
		{
			//  Card size error
			DEBUGPRINT ( 1, "vmcfs: Error size of vmc file %s is incompatible\n", devname );
			errcode = VMCFS_ERR_VMC_SIZE;
			goto mountAbort;
		}

		DEBUGPRINT ( 4, "vmcfs: Image file Info: Number of pages       : %d\n", g_Vmc_Image[ f->unit ].total_pages );
		DEBUGPRINT ( 4, "vmcfs: Image file Info: Size of a cluster     : %d bytes\n", g_Vmc_Image[ f->unit ].cluster_size );
		DEBUGPRINT ( 4, "vmcfs: Image file Info: ECC shunk found       : %s\n", g_Vmc_Image[ f->unit ].ecc_flag ? "YES" : "NO" );
	}

	if ( g_Vmc_Image[ f->unit ].formated == FALSE ){
		errcode = VMCFS_ERR_NOT_FORMATED;
		goto mountAbort;
	}

	return 0;
}

//----------------------------------------------------------------------------
// Unmount a vmc file previously mounted with fileXioMount(
//----------------------------------------------------------------------------
int Vmc_Umount ( iop_file_t* f, const char* fsname )
{

	DEBUGPRINT ( 2, "vmcfs: UnMount %s at mount point: %d\n", fsname, f->unit );

	close ( g_Vmc_Image[ f->unit ].fd );
	g_Vmc_Image[ f->unit ].fd = VMCFS_ERR_NOT_MOUNT;

	return 0;

}

//----------------------------------------------------------------------------
// Not Implemented for the moment.
//----------------------------------------------------------------------------
int Vmc_Lseek64 ( iop_file_t* f, long long offset, int whence )
{
	
	return VMCFS_ERR_IMPLEMENTED;
	
}


//----------------------------------------------------------------------------
// Control command.
// DEVCTL_VMCFS_CLEAN   :  Set as free all fat cluster corresponding to a none existing object. ( Object are just marked as none existing but not removed from fat table when rmdir or remove fonctions are call. This allow to recover a deleted file. )
// DEVCTL_VMCFS_CKFREE  :  Check free space available on vmc file. 
//----------------------------------------------------------------------------
int Vmc_Devctl ( iop_file_t* f, const char* path, int cmd, void *arg, unsigned int arglen, void *buf, unsigned int buflen )
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	switch ( cmd ) 
	{

		case DEVCTL_VMCFS_CLEAN:
			{

				Vmc_Clean ( f->unit );

			}
			break;

		case DEVCTL_VMCFS_CKFREE:
			{

				unsigned int free_space = Vmc_Checkfree ( f->unit );

				return free_space;

			}
			break;

		default:

			DEBUGPRINT ( 1, "vmcfs: Unrecognized devctl command %d\n", cmd );
			break;

	}

	return VMCFS_ERR_NO;

}


//----------------------------------------------------------------------------
// Not Implemented for the moment.
//----------------------------------------------------------------------------
int Vmc_Symlink ( iop_file_t* f, const char* old, const char* new )
{
	
	return VMCFS_ERR_IMPLEMENTED;
	
}


//----------------------------------------------------------------------------
// Not Implemented for the moment.
//----------------------------------------------------------------------------
int Vmc_Readlink ( iop_file_t* f, const char* path, char* buf, unsigned int buf_len )
{
	
	return VMCFS_ERR_IMPLEMENTED;
	
}


//----------------------------------------------------------------------------
// Not Implemented for the moment.
//----------------------------------------------------------------------------
int Vmc_Ioctl2 ( iop_file_t* f, int cmd, void *arg, unsigned int arglen,	void *buf, unsigned int buflen )
{
	
	return VMCFS_ERR_IMPLEMENTED;
	
}


// Extanded ioctl fonctions.


//----------------------------------------------------------------------------
// Recover an object in vmc image after removing it.
// Not working properly when object have been overwrited
//----------------------------------------------------------------------------
int Vmc_Recover ( int unit, const char* path1 ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: recover %s\n", path1 );

	if ( g_Vmc_Image[ unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_recoverProf ) 

	struct direntry     dirent;
	struct gen_privdata gendata;

	gendata.fd                = g_Vmc_Image[ unit ].fd;
	gendata.first_allocatable = g_Vmc_Image[ unit ].header.first_allocatable;
	gendata.last_allocatable  = g_Vmc_Image[ unit ].header.last_allocatable;

	memcpy ( gendata.indir_fat_clusters, g_Vmc_Image[ unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

	// Make a local copy of path.
	char* path = malloc ( strlen ( path1 ) - strlen ( "vmc0:" ) + 1 );
	memcpy ( path, path1 + strlen ( "vmc0:" ), strlen ( path1 ) - strlen ( "vmc0:" ) + 1 ); 

	if ( path[ strlen ( path ) - 1 ] == '/' ) 
		path[ strlen ( path ) - 1 ]= '\0';

	unsigned int dirent_cluster = getDirentryFromPath ( &dirent, path, &gendata, unit );

	if ( dirent_cluster == ROOT_CLUSTER )
	{

		DEBUGPRINT ( 2, "vmcfs: recover failed. Root directory is protected.\n" );

		free( path );

		PROF_END ( vmc_recoverProf ) 

		return -1;

	}
	else if ( dirent_cluster == NOFOUND_CLUSTER )
	{

		DEBUGPRINT ( 2, "vmcfs: recover failed. %s not found.\n", path1 );

		free( path );

		PROF_END ( vmc_recoverProf ) 

		return -1;

	}

	struct direntry     parent;
	struct gen_privdata parent_gendata;
	unsigned int        parent_cluster = 0;

	parent_gendata.fd                = g_Vmc_Image[ unit ].fd;
	parent_gendata.first_allocatable = g_Vmc_Image[ unit ].header.first_allocatable;
	parent_gendata.last_allocatable  = g_Vmc_Image[ unit ].header.last_allocatable;

	memcpy ( parent_gendata.indir_fat_clusters, g_Vmc_Image[ unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

	// Find name of file, and name of parent
	char* filename = strrchr ( path, '/' );

	filename[ 0 ] = '\0';
	filename++;

	DEBUGPRINT ( 4, "vmcfs: Checking attributs parent directory %s\n", path );

	if ( path[ 0 ] == '\0' ) 
	{

		//  get the root directories entry
		parent_cluster = getDirentryFromPath ( &parent, ".", &parent_gendata, unit );

	}
	else
	{

		//  get the folder entry for the parent
		parent_cluster = getDirentryFromPath ( &parent, path, &parent_gendata, unit );

	}

	if ( parent_cluster == NOFOUND_CLUSTER )
	{

		DEBUGPRINT ( 3, "vmcfs: Unable to recover %s. Parent directory not found.\n", path1 );

		free ( path );

		PROF_END ( vmc_recoverProf ) 

		return -1;

	}

	DEBUGPRINT ( 6, "vmcfs: Parent Information.\n" );
	DEBUGPRINT ( 6, "vmcfs: parent_cluster  = %u\n", parent_cluster );
	DEBUGPRINT ( 6, "vmcfs: dir_cluster    = %u\n", parent.cluster );
	DEBUGPRINT ( 6, "vmcfs: dirent.name    = %s\n", parent.name );
	DEBUGPRINT ( 6, "vmcfs: dirent.length  = %u\n", parent.length );
	DEBUGPRINT ( 6, "vmcfs: dirent.mode    = %X\n", parent.mode );
	DEBUGPRINT ( 6, "vmcfs: dirent_page    = %i\n", parent_gendata.dirent_page );

	if ( ! ( parent.mode & DF_EXISTS ) )
	{

		DEBUGPRINT ( 3, "vmcfs: Unable to restore %s. Parent directory %s is hidden.\n", path1, path );

		free ( path );

		PROF_END ( vmc_recoverProf ) 

		return -1;

	}

	if ( dirent.mode & DF_DIRECTORY ) // directory case
	{

		if ( dirent.mode & DF_EXISTS ) 
		{

			DEBUGPRINT ( 2, "vmcfs: recover failed on %s. Directory allready exist.\n", path1 );

			free( path );

			PROF_END ( vmc_recoverProf ) 

			return -1;

		}
		else
		{

			DEBUGPRINT ( 8, "vmcfs: recover directory %s allready exist but is hidden. Changing attributs.\n", path1 );

			DEBUGPRINT ( 8, "vmcfs: Following fat table cluster %u\n", dirent.cluster );

			unsigned int pseudo_entry_cluster = getFatEntry ( gendata.fd, dirent.cluster, gendata.indir_fat_clusters, FAT_VALUE );

			DEBUGPRINT ( 8, "vmcfs: Changing cluster mask of fat table cluster %u.\n", pseudo_entry_cluster );

			// change cluster mask of the direntry
			setFatEntry ( gendata.fd, dirent.cluster, pseudo_entry_cluster, gendata.indir_fat_clusters, FAT_SET );

			DEBUGPRINT ( 8, "vmcfs: Changing direntry %s attributs.\n", path1 );

			//  Update time stamp, and set dirent.mode to exist flag
			dirent.mode	= dirent.mode | DF_EXISTS;
			getPs2Time ( &dirent.created );
			getPs2Time ( &dirent.modified );
			writePage ( gendata.fd, ( unsigned char* ) &dirent, ( dirent_cluster + gendata.first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster + gendata.dirent_page );

			DEBUGPRINT ( 8, "vmcfs: Restoring EOF cluster at %u.\n", pseudo_entry_cluster );

			setFatEntry ( gendata.fd, pseudo_entry_cluster, EOF_CLUSTER, gendata.indir_fat_clusters, FAT_SET );

			struct direntry pseudo_entries;

			DEBUGPRINT ( 8, "vmcfs: Updating pseudo entries time stamps at cluster %u / page %u.\n", dirent.cluster + gendata.first_allocatable, ( dirent.cluster + gendata.first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster );

			//  Update time stamp of '.' entry
			readPage ( gendata.fd, ( unsigned char* ) &pseudo_entries, ( dirent.cluster + gendata.first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster );
			getPs2Time ( &pseudo_entries.created );
			getPs2Time ( &pseudo_entries.modified );
			writePage ( gendata.fd, ( unsigned char* ) &pseudo_entries, ( dirent.cluster + gendata.first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster );

			DEBUGPRINT ( 8, "vmcfs: Updating pseudo entries time stamps at cluster %u / page %u.\n", dirent.cluster + gendata.first_allocatable, ( dirent.cluster + gendata.first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster + 1 );

			//  Update time stamp of '..' entry
			readPage ( gendata.fd, ( unsigned char* ) &pseudo_entries, ( dirent.cluster + gendata.first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster + 1 );
			getPs2Time ( &pseudo_entries.created );
			getPs2Time ( &pseudo_entries.modified );
			writePage ( gendata.fd, ( unsigned char* ) &pseudo_entries, ( dirent.cluster + gendata.first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster + 1 );

			DEBUGPRINT ( 2, "vmcfs: Directory %s recovered.\n", path1 );

			free( path );

			goto end;	

		}
		
	}
	else // file case
	{

		if ( dirent.mode & DF_EXISTS ) 
		{

			DEBUGPRINT ( 2, "vmcfs: recover failed on %s. File allready exist.\n", path );

			free( path );

			PROF_END ( vmc_recoverProf ) 

			return -1;

		}
		else
		{

			unsigned int current_cluster = 0;
			unsigned int last_cluster    = dirent.cluster;

			DEBUGPRINT ( 8, "vmcfs: Restoring fat table clusters of file %s\n", filename );

			while ( 1 ) 
			{

				current_cluster = getFatEntry ( gendata.fd, last_cluster, gendata.indir_fat_clusters, FAT_VALUE );

				if ( current_cluster == FREE_CLUSTER ) 
				{

					// FREE_CLUSTER mean last cluster of the direntry is found
					DEBUGPRINT ( 8, "vmcfs: Last cluster of file at %u\n", last_cluster );
			
					DEBUGPRINT ( 8, "vmcfs: Restoring End Of File at fat table cluster %u\n", last_cluster );

					setFatEntry ( gendata.fd, last_cluster, EOF_CLUSTER, gendata.indir_fat_clusters, FAT_SET );

					break;

				}
				else if ( current_cluster == EOF_CLUSTER ) 
				{

					// EOF_CLUSTER mean nothing to create or error, so goto end
					DEBUGPRINT ( 3, "vmcfs: Error. EOF_CLUSTER found !!!\n" );

					free( path );

					goto end;

				}
				else
				{

					// Otherwise set cluster as free
					DEBUGPRINT ( 10, "vmcfs: Testing cluster %u ... value is %u\n", last_cluster, current_cluster );

					DEBUGPRINT ( 8, "vmcfs: Restoring cluster mask at fat table cluster %u\n", last_cluster );

					setFatEntry ( gendata.fd, last_cluster, current_cluster, gendata.indir_fat_clusters, FAT_SET );

				}

				last_cluster = current_cluster;

			}

			DEBUGPRINT ( 8, "vmcfs: Restoring direntry at cluster %u / page %u\n", dirent_cluster + gendata.first_allocatable, ( dirent.cluster + gendata.first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster + gendata.dirent_page );

			dirent.mode = dirent.mode | DF_EXISTS;
			getPs2Time ( &dirent.created  );
			getPs2Time ( &dirent.modified );
			writePage ( gendata.fd, ( unsigned char* ) &dirent, ( dirent_cluster + gendata.first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster + gendata.dirent_page );

			free( path );

			DEBUGPRINT ( 3, "vmcfs: File %s restored.\n", path1 );

		}

	}

end:

	PROF_END ( vmc_recoverProf ) 

	return 0;

}


//----------------------------------------------------------------------------
// Check actual free space of the vmc file.
//----------------------------------------------------------------------------
unsigned int Vmc_Checkfree ( int unit ) 
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: check free %d\n", unit );

	if ( g_Vmc_Image[ unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_checkfreeProf ) 

	int                 i                = 0;
	unsigned int        cluster_value    = 0;
	unsigned int        cluster_mask     = 0;
	unsigned int        free_space       = 0;
	unsigned int        free_cluster_num = 0;
	struct gen_privdata gendata;

	gendata.fd                = g_Vmc_Image[ unit ].fd;
	gendata.first_allocatable = g_Vmc_Image[ unit ].header.first_allocatable;
	gendata.last_allocatable  = g_Vmc_Image[ unit ].header.last_allocatable;

	memcpy ( gendata.indir_fat_clusters, g_Vmc_Image[ unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

	for ( i = gendata.first_allocatable; i < gendata.last_allocatable; i++ ) 
	{

		cluster_value = getFatEntry ( gendata.fd, i - gendata.first_allocatable, gendata.indir_fat_clusters, FAT_VALUE );

		if ( cluster_value == FREE_CLUSTER ) 
		{

			DEBUGPRINT ( 10, "vmcfs: Testing cluster %d ... value is FREE_CLUSTER\n", i );

			DEBUGPRINT ( 6, "vmcfs: Free cluster found at %d\n", i );

			free_cluster_num++;

		}
		else if ( cluster_value == EOF_CLUSTER ) 
		{

			DEBUGPRINT ( 10, "vmcfs: Testing cluster %d ... value is EOF_CLUSTER\n", i );

		}
		else
		{

			DEBUGPRINT ( 10, "vmcfs: Testing cluster %d ... value is %u\n", i, cluster_value );
			
			cluster_mask = getFatEntry ( gendata.fd, i - gendata.first_allocatable, gendata.indir_fat_clusters, FAT_MASK );
			
			if ( cluster_mask != MASK_CLUSTER )
			{

				DEBUGPRINT ( 6, "vmcfs: Free cluster found at %d\n", i );

				free_cluster_num++;

			}

		}

	}
	
	free_space = free_cluster_num * g_Vmc_Image[ unit ].cluster_size;

	PROF_END ( vmc_checkfreeProf ) 

	DEBUGPRINT ( 3, "vmcfs: Total free space: %u\n", free_space );

	return free_space;

}


//----------------------------------------------------------------------------
// Clean unused cluster of the vmc file.
//----------------------------------------------------------------------------
int Vmc_Clean ( int unit )
{

	if ( !g_Vmc_Initialized ) 
		return VMCFS_ERR_INITIALIZED;

	DEBUGPRINT ( 1, "vmcfs: clean %d\n", unit );

	if ( g_Vmc_Image[ unit ].fd < 0 ) 
		return VMCFS_ERR_NOT_MOUNT;

	if ( g_Vmc_Image[ unit ].formated == FALSE ) 
		return VMCFS_ERR_NOT_FORMATED;

	PROF_START ( vmc_cleanProf ) 

	int                 i             = 0;
	unsigned int        cluster_value = 0;
	unsigned int        cluster_mask  = 0;
	int                 object_remove = FALSE;
	struct gen_privdata gendata;

	gendata.fd                = g_Vmc_Image[ unit ].fd;
	gendata.first_allocatable = g_Vmc_Image[ unit ].header.first_allocatable;
	gendata.last_allocatable  = g_Vmc_Image[ unit ].header.last_allocatable;

	memcpy ( gendata.indir_fat_clusters, g_Vmc_Image[ unit ].header.indir_fat_clusters, sizeof ( unsigned int ) * 32 );

	for ( i = gendata.first_allocatable; i < gendata.last_allocatable; i++ ) 
	{

		cluster_value = getFatEntry ( gendata.fd, i - gendata.first_allocatable, gendata.indir_fat_clusters, FAT_VALUE );

		if ( cluster_value == FREE_CLUSTER ) 
		{

			DEBUGPRINT ( 10, "vmcfs: Testing cluster %d ... value is FREE_CLUSTER\n", i );

		}
		else if ( cluster_value == EOF_CLUSTER ) 
		{

			DEBUGPRINT ( 10, "vmcfs: Testing cluster %d ... value is EOF_CLUSTER\n", i );

		}
		else
		{

			DEBUGPRINT ( 10, "vmcfs: Testing cluster %d ... value is %u\n", i, cluster_value );
			
			cluster_mask = getFatEntry ( gendata.fd, i - gendata.first_allocatable, gendata.indir_fat_clusters, FAT_MASK );
			
			if ( cluster_mask != MASK_CLUSTER )
			{

				DEBUGPRINT ( 6, "vmcfs: Setting cluster %d as free cluster.\n", i );

				setFatEntry ( gendata.fd, i - gendata.first_allocatable, FREE_CLUSTER, gendata.indir_fat_clusters, FAT_SET );
				
				object_remove = TRUE;

			}

		}

	}

	return 0;

}
