#include "vmc.h"


// Misc fonctions 

//----------------------------------------------------------------------------
// Search a direntry corresponding to a path
//----------------------------------------------------------------------------
unsigned int getDirentryFromPath ( struct direntry* retval, const char* path, struct gen_privdata* gendata, int unit ) 
{

	PROF_START ( getDirentryFromPathProf );

	DEBUGPRINT ( 6, "vmcfs: Searching Direntry corresponding to path: %s\n", path );

	// Skip past the first slash if they opened a file such as
	// vmc0: / file.txt ( which means the path passed here will be / file.txt, we
	// want to skip that first slash, to ease comparisons.
	int pathoffset = 0;

	if ( path[ 0 ] ==  '/' ) 
		pathoffset = 1;

	if ( ( path[ 0 ] ==  '/' || path[ 0 ] == '.' ) && path[ 1 ] == '\0' ) // rootdirectory
	{

		gendata->dirent_page = 0;
		readPage ( gendata->fd, ( unsigned char* ) retval, gendata->first_allocatable * g_Vmc_Image[ unit ].header.pages_per_cluster );

		PROF_END ( getDirentryFromPathProf );

		return ROOT_CLUSTER;

	}

	struct direntry dirent;		        // Our temporary directory entry

	int status                   = 0; // The status of our search, if 0 at the end, we didn't find path
	unsigned int current_cluster = 0;	// The cluster we are currently scanning for directory entries
	int length                   = 1;	// The number of items in the current directory
	int i                        = 0;

	// Our main loop that goes through files / folder searching for the right one
	for ( i = 0; i < length; i++ ) 
	{

		gendata->dirent_page = i % g_Vmc_Image[ unit ].header.pages_per_cluster;

		DEBUGPRINT ( 6, "vmcfs: Reading in allocatable cluster %u / page %u\n", ( current_cluster + gendata->first_allocatable ), ( current_cluster + gendata->first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster + gendata->dirent_page );

		// Reads either the first or second page of a cluster, depending
		// on the value currently stored in i
		readPage ( gendata->fd, ( unsigned char* ) &dirent, ( current_cluster + gendata->first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster + gendata->dirent_page );

		// Check if this was the first entry ( aka the rootdirectory ) 
		if ( i == 0 && current_cluster == 0 ) 
			length = dirent.length;

		DEBUGPRINT ( 5, "vmcfs: Direntry Informations\n" );
		DEBUGPRINT ( 5, "vmcfs: Object Type    : %s\n", ( dirent.mode & DF_DIRECTORY ) ?"Folder":"File" );
		DEBUGPRINT ( 5, "vmcfs: Object Name    : %s\n", dirent.name );
		DEBUGPRINT ( 5, "vmcfs: Object Exists  : %s\n", ( dirent.mode & DF_EXISTS ) ?"Yes":"No" );
		DEBUGPRINT ( 5, "vmcfs: Object Length  : %u\n", dirent.length );
		DEBUGPRINT ( 5, "vmcfs: Object Cluster : %u\n", dirent.cluster );

		// Now that we have a pages worth of data, check if it is the
		// Directory we are searching for.

		if ( memcmp ( dirent.name, path + pathoffset, strlen ( dirent.name )  ) == 0 ) 
		{

			// Increase the path offset by the length of the directory
			pathoffset += strlen ( dirent.name );

			// If the next item in the pathname is the null terminator, 
			// we must be at the end of the path string, and that means
			// we found the correct entry, so we can break.
			if ( path[ pathoffset ] == '\0' || ( path[ pathoffset ] == '/' && path[ pathoffset + 1 ] == '\0' )  ) 
			{

				DEBUGPRINT ( 6, "vmcfs: Breaking from function\n" );
				DEBUGPRINT ( 6, "vmcfs: dir_cluster = %u\n", dirent.cluster );
				DEBUGPRINT ( 6, "vmcfs: dirent.name = %s\n", dirent.name );
				DEBUGPRINT ( 6, "vmcfs: dirent.length = %u\n", dirent.length );

				status = 1;

				break;

			}
			// Otherwise we found the subfolder, but not the
			// requested entry, keep going
			else
			{

				DEBUGPRINT ( 6, "vmcfs: Recursing into subfolder\n" );
				DEBUGPRINT ( 6, "vmcfs: dir_cluster = %u\n", dirent.cluster );
				DEBUGPRINT ( 6, "vmcfs: dirent.name = %s\n", dirent.name );
				DEBUGPRINT ( 6, "vmcfs: dirent.length = %u\n", dirent.length );

				i               = -1;             // will be 0 when we continue, essentially starting the loop over again
				current_cluster = dirent.cluster; // dirent.cluster refer to fat table and current_cluster to allocatable place
				length          = dirent.length;  // set length to current directory length
				pathoffset++;                     // add one to skip past the / in the folder name

				continue;

			}

		}

		// If we just read the second half of a cluster, we need to set
		// current_cluster to the next cluster in the chain.
		if ( i % g_Vmc_Image[ unit ].header.pages_per_cluster ) 
		{

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

		}

	}

	// When we get here, that means one of two things:
	// 1 ) We found the requested folder / file
	// 2 ) We searched through all files / folders, and could not find it.
	// To determine which one it was, check the status variable.
	if ( status == 0 ) 
	{

		PROF_END ( getDirentryFromPathProf ) 

		return NOFOUND_CLUSTER;

	}

	// Copy the last directory entry's contents into 'retval'
	memcpy ( retval, &dirent, sizeof ( dirent )  );

	PROF_END ( getDirentryFromPathProf ) 

	// Return the cluster where the desired directory entry can be found
	return current_cluster;

}


//----------------------------------------------------------------------------
// Add 2 pseudo entries for a new directory
//----------------------------------------------------------------------------
unsigned int addPseudoEntries ( struct gen_privdata* gendata, struct direntry* parent, int unit )
{

	// Get a free cluster we can use to store the entries '.' and '..'
	unsigned int pseudo_entries_cluster = getFreeCluster ( gendata, unit );

	if ( pseudo_entries_cluster == ERROR_CLUSTER ) 
	{

		DEBUGPRINT ( 2, "vmcfs: Not enough free space to add pseudo entries.  Aborting.\n" );

		return ERROR_CLUSTER;

	}

	// Create the first 2 psuedo entries for the folder, and write them
	DEBUGPRINT ( 5, "vmcfs: Adding pseudo entries into fat table cluster %u\n", pseudo_entries_cluster );

	struct direntry pseudo_entries;

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

	// fill pseudo entries
	strcpy ( pseudo_entries.name, "." );
	pseudo_entries.dir_entry = parent->length;
	pseudo_entries.length    = 0;
	pseudo_entries.cluster   = parent->cluster;
	pseudo_entries.mode      = DF_EXISTS | DF_0400 | DF_DIRECTORY | DF_READ | DF_WRITE | DF_EXECUTE; // 0x8427

	getPs2Time ( &pseudo_entries.created  );
	getPs2Time ( &pseudo_entries.modified );

	// write first pseudo entry
	writePage ( gendata->fd, ( unsigned char* ) &pseudo_entries, ( pseudo_entries_cluster + gendata->first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster );

	strcpy ( pseudo_entries.name, ".." );
	pseudo_entries.dir_entry = 0;
	pseudo_entries.cluster   = 0;

	// write second pseudo entry
	writePage ( gendata->fd, ( unsigned char* ) &pseudo_entries, ( pseudo_entries_cluster + gendata->first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster + 1 );

	// set cluster as EOF
	setFatEntry ( gendata->fd, pseudo_entries_cluster, EOF_CLUSTER, gendata->indir_fat_clusters, FAT_SET );

	return pseudo_entries_cluster;
	
}


//----------------------------------------------------------------------------
// Add an object into vmc image.
// This fonction get a free cluster where it can insert the object, insert it and return the cluster number.
// Object can be a file or a folder.
//----------------------------------------------------------------------------
unsigned int addObject ( struct gen_privdata* gendata, unsigned int parent_cluster, struct direntry* parent, struct direntry* dirent, int unit ) 
{

	int          i                = 0;
	unsigned int retval           = ERROR_CLUSTER;
	unsigned int current_cluster  = parent->cluster;

	// Find to the last cluster in the list of files / folder to add our new object after it
	for ( i = 0; i < parent->length; i += g_Vmc_Image[ unit ].header.pages_per_cluster ) 
	{

		DEBUGPRINT ( 6, "vmcfs: Following fat table cluster: %u\n", current_cluster );

		if ( getFatEntry ( gendata->fd, current_cluster, gendata->indir_fat_clusters, FAT_VALUE ) == EOF_CLUSTER ) 
		{

			DEBUGPRINT ( 6, "vmcfs: Last used cluster in fat table %u\n", current_cluster );
			break;

		}

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

	}

	// Check if we need a new cluster or if we can add our object at the end of an allocatable cluster
	if ( parent->length % g_Vmc_Image[ unit ].header.pages_per_cluster == 0 ) 	// if its even, we need a new cluster for our file
	{

		// Get a free cluster because our object require an additional cluster
		unsigned int nextfree_cluster = getFreeCluster ( gendata, unit );

		if ( nextfree_cluster == ERROR_CLUSTER ) 
		{

			DEBUGPRINT ( 2, "vmcfs: Not enough free space.  Aborting.\n" );

			return ERROR_CLUSTER;

		}

		DEBUGPRINT ( 6, "vmcfs: Added new object in fat table cluster %u ( Page %u ) \n", current_cluster, current_cluster * g_Vmc_Image[ unit ].header.pages_per_cluster );

		setFatEntry ( gendata->fd, current_cluster, nextfree_cluster, gendata->indir_fat_clusters, FAT_SET ); // update the last cluster in the directory entry list to point to our new cluster
		setFatEntry ( gendata->fd, nextfree_cluster, EOF_CLUSTER, gendata->indir_fat_clusters, FAT_SET );     // set the free cluster we just found to be EOF

		// If the object is a folder, we have to add 2 psuedoentries
		if ( dirent->mode & DF_DIRECTORY ) 
		{

			// Link the new folder to the entries we just created
			dirent->cluster = addPseudoEntries( gendata, parent, unit );
			
			if ( dirent->cluster == ERROR_CLUSTER )
				return ERROR_CLUSTER;

			// Set the length of the directory to 2, for the 2 psuedoentries we just added
			dirent->length  = 2;

		}

		// write the page containing our direntry object information
		writePage ( gendata->fd, ( unsigned char* ) dirent, ( nextfree_cluster + gendata->first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster );

		// now we need to update the length of the parent directory
		parent->length++;
		writePage ( gendata->fd, ( unsigned char* ) parent, ( parent_cluster + gendata->first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster + gendata->dirent_page );

		gendata->dirent_page = 0;
		retval               = nextfree_cluster;

	}
	else	// otherwise we can just add the directory entry to the end of the last cluster
	{

		DEBUGPRINT ( 5, "vmcfs: Added new object in fat table cluster %u ( Page %u ) \n", current_cluster, current_cluster * g_Vmc_Image[ unit ].header.pages_per_cluster + 1 );

		// If the object is a folder, we have to add 2 psuedoentries
		if ( dirent->mode & DF_DIRECTORY ) 
		{

			// Link the new folder to the entries we just created
			dirent->cluster = addPseudoEntries( gendata, parent, unit );
			
			if ( dirent->cluster == ERROR_CLUSTER )
				return ERROR_CLUSTER;

			// Set the length of the directory to 2, for the 2 psuedoentries we just added
			dirent->length  = 2;

		}

		// write the page containing our direntry object information
		writePage ( gendata->fd, ( unsigned char* ) dirent, ( current_cluster + gendata->first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster + 1 );// write the page containing our directory information ( + 1 because we are writing the second page in the cluster ) 

		// now we need to update the length of the parent directory
		parent->length++;
		writePage ( gendata->fd, ( unsigned char* ) parent, ( parent_cluster + gendata->first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster + gendata->dirent_page );

		gendata->dirent_page = 1;
		retval               = current_cluster;

	}

	return retval;

}


//----------------------------------------------------------------------------
// Remove an object from vmc image.
// Object can be a file or a directory.
// Follow fat table to find the last cluster of the direntry chain cluster. ( EOF_CLUSTER )
// Change bit mask of tested cluster if it's not EOF. ( only for files )
// Set as free the last cluster of the direntry.
// Finaly, set deleted flag of the direntry. ( DF_EXISTS flag )
//----------------------------------------------------------------------------
void removeObject ( struct gen_privdata* gendata, unsigned int dirent_cluster, struct direntry* dirent, int unit )
{

	unsigned int current_cluster = 0;
	unsigned int last_cluster    = dirent->cluster;

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

	while ( 1 ) 
	{

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

		if ( current_cluster == FREE_CLUSTER ) 
		{

			// FREE_CLUSTER mean nothing to remove or error, so return
			DEBUGPRINT ( 10, "vmcfs: Testing cluster %u ... value is FREE_CLUSTER\n", last_cluster );
			
			return;

		}
		else if ( current_cluster == EOF_CLUSTER ) 
		{

			// EOF_CLUSTER mean last cluster of the direntry is found
			DEBUGPRINT ( 3, "vmcfs: Last cluster of direntry at %u\n", last_cluster );

			break;

		}
		else
		{

			// Otherwise change bit mask of tested cluster
			DEBUGPRINT ( 10, "vmcfs: Testing cluster %u ... value is %u\n", last_cluster, current_cluster );

			setFatEntry ( gendata->fd, last_cluster, current_cluster, gendata->indir_fat_clusters, FAT_RESET );

		}

		last_cluster = current_cluster;

	}

	// Set last cluster of direntry as free.
	setFatEntry ( gendata->fd, last_cluster, FREE_CLUSTER, gendata->indir_fat_clusters, FAT_RESET ); // set the last cluster of the file as free

	// Set object as deleted. ( Remove DF_EXISTS flag )
	dirent->mode = dirent->mode ^ DF_EXISTS;
	writePage ( gendata->fd, ( unsigned char* ) dirent, ( dirent_cluster + gendata->first_allocatable ) * g_Vmc_Image[ unit ].header.pages_per_cluster + gendata->dirent_page );

}


//----------------------------------------------------------------------------
// Return a free cluster.
//----------------------------------------------------------------------------
unsigned int getFreeCluster ( struct gen_privdata* gendata, int unit ) 
{

	unsigned int i            = 0;
	unsigned int value        = 0;
	unsigned int cluster_mask = MASK_CLUSTER;

	for ( i = g_Vmc_Image[ unit ].last_free_cluster; i < gendata->last_allocatable; i++ ) 
	{

		value = getFatEntry ( gendata->fd, i - gendata->first_allocatable, gendata->indir_fat_clusters, FAT_VALUE );

		if ( value == FREE_CLUSTER ) 
		{

			DEBUGPRINT ( 10, "vmcfs: Testing fat table cluster %d ... value is FREE_CLUSTER\n", i - gendata->first_allocatable );

			DEBUGPRINT ( 6, "vmcfs: Free cluster found at %d in fat table\n", i - gendata->first_allocatable );
			g_Vmc_Image[ unit ].last_free_cluster = i;

			return ( i - gendata->first_allocatable );

		}
		else if ( value == EOF_CLUSTER ) 
		{

			DEBUGPRINT ( 10, "vmcfs: Testing fat table cluster %d ... value is EOF_CLUSTER\n", i - gendata->first_allocatable );

		}
		else
		{

			DEBUGPRINT ( 10, "vmcfs: Testing fat table cluster %d ... value is %d\n", i - gendata->first_allocatable, 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 in fat table\n", i - gendata->first_allocatable );
				g_Vmc_Image[ unit ].last_free_cluster = i;

				return ( i - gendata->first_allocatable );

			}

		}

	}

	return ERROR_CLUSTER;

}


//----------------------------------------------------------------------------
// Set default specification to superblock header when card is not formated
//----------------------------------------------------------------------------
int setDefaultSpec ( int unit ) 
{

	DEBUGPRINT ( 4, "vmcfs: SuperBlock set by default\n" );

	memset ( &g_Vmc_Image[ unit ].header, g_Vmc_Image[ unit ].erase_byte, sizeof ( struct superblock )  );
	
	strcpy ( g_Vmc_Image[ unit ].header.magic, "Sony PS2 Memory Card Format 1.2.0.0" );

	DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: magic[40]             : %s\n"   , g_Vmc_Image[ unit ].header.magic                  );

	g_Vmc_Image[ unit ].header.page_size = 0x200;
	g_Vmc_Image[ unit ].header.pages_per_cluster = 0x2;
	g_Vmc_Image[ unit ].header.pages_per_block = 0x10;
	g_Vmc_Image[ unit ].header.unused0 = 0xFF00;

	g_Vmc_Image[ unit ].cluster_size = g_Vmc_Image[ unit ].header.page_size * g_Vmc_Image[ unit ].header.pages_per_cluster;
	g_Vmc_Image[ unit ].erase_byte   = 0xFF;
	g_Vmc_Image[ unit ].last_idc     = EOF_CLUSTER;
	g_Vmc_Image[ unit ].last_cluster = EOF_CLUSTER;

	if ( g_Vmc_Image[ unit ].card_size %( g_Vmc_Image[ unit ].header.page_size + 0x10 ) == 0 ) 
	{

		g_Vmc_Image[ unit ].ecc_flag = TRUE;
		g_Vmc_Image[ unit ].total_pages = g_Vmc_Image[ unit ].card_size / ( g_Vmc_Image[ unit ].header.page_size + 0x10 );
		g_Vmc_Image[ unit ].header.clusters_per_card = g_Vmc_Image[ unit ].card_size / (  ( g_Vmc_Image[ unit ].header.page_size + 0x10 ) * g_Vmc_Image[ unit ].header.pages_per_cluster );

	}
	else if ( g_Vmc_Image[ unit ].card_size %g_Vmc_Image[ unit ].header.page_size == 0 ) 
	{

		g_Vmc_Image[ unit ].ecc_flag = FALSE;
		g_Vmc_Image[ unit ].total_pages = g_Vmc_Image[ unit ].card_size / g_Vmc_Image[ unit ].header.page_size;
		g_Vmc_Image[ unit ].header.clusters_per_card = g_Vmc_Image[ unit ].card_size / ( g_Vmc_Image[ unit ].header.page_size * g_Vmc_Image[ unit ].header.pages_per_cluster );

	}
	else
	{

		// Size error
		return 0;

	}

	g_Vmc_Image[ unit ].header.mc_type = 0x2;
	g_Vmc_Image[ unit ].header.mc_flag = 0x2B;

	DEBUGPRINT ( 4, "vmcfs: Image file Info: Number of pages       : %d\n", g_Vmc_Image[ unit ].total_pages );
	DEBUGPRINT ( 4, "vmcfs: Image file Info: Size of a cluster     : %d bytes\n", g_Vmc_Image[ unit ].cluster_size );
	DEBUGPRINT ( 4, "vmcfs: Image file Info: ECC shunk found       : %s\n", g_Vmc_Image[ unit ].ecc_flag ? "YES" : "NO" );
	DEBUGPRINT ( 3, "vmcfs: Image file Info: Vmc card type         : %s MemoryCard.\n", ( g_Vmc_Image[ unit ].header.mc_type == PSX_MEMORYCARD ? "PSX" : ( g_Vmc_Image[ unit ].header.mc_type == PS2_MEMORYCARD ? "PS2" : "PDA" )  )  );
	DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: page_size             : 0x%02x\n", g_Vmc_Image[ unit ].header.page_size              );
	DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: pages_per_cluster     : 0x%02x\n", g_Vmc_Image[ unit ].header.pages_per_cluster      );
	DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: pages_per_block       : 0x%02x\n", g_Vmc_Image[ unit ].header.pages_per_block        );
	DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: clusters_per_card     : 0x%02x\n", g_Vmc_Image[ unit ].header.clusters_per_card      );

	g_Vmc_Image[ unit ].header.first_allocatable = (  (  (  ( g_Vmc_Image[ unit ].total_pages - 1 ) * ( g_Vmc_Image[ unit ].cluster_size / g_Vmc_Image[ unit ].header.page_size )  ) / g_Vmc_Image[ unit ].cluster_size ) + 1 ) + 8 + (  ( g_Vmc_Image[ unit ].total_pages - 1 ) / ( g_Vmc_Image[ unit ].total_pages * g_Vmc_Image[ unit ].header.pages_per_cluster * ( g_Vmc_Image[ unit ].cluster_size / g_Vmc_Image[ unit ].header.page_size )  )  ) + 1;
	g_Vmc_Image[ unit ].header.last_allocatable = ( g_Vmc_Image[ unit ].header.clusters_per_card - g_Vmc_Image[ unit ].header.first_allocatable ) - (  ( g_Vmc_Image[ unit ].header.pages_per_block / g_Vmc_Image[ unit ].header.pages_per_cluster ) * g_Vmc_Image[ unit ].header.pages_per_cluster );
	g_Vmc_Image[ unit ].header.root_cluster = 0;
	
	g_Vmc_Image[ unit ].header.backup_block1 = ( g_Vmc_Image[ unit ].total_pages / g_Vmc_Image[ unit ].header.pages_per_block ) - 1;
	g_Vmc_Image[ unit ].header.backup_block2 = g_Vmc_Image[ unit ].header.backup_block1 - 1;
	memset ( g_Vmc_Image[ unit ].header.unused1, g_Vmc_Image[ unit ].erase_byte, 8 );

	DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: first_allocatable     : 0x%02x\n", g_Vmc_Image[ unit ].header.first_allocatable      );
	DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: last_allocatable      : 0x%02x\n", g_Vmc_Image[ unit ].header.last_allocatable       );
	DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: root_cluster          : 0x%02x\n", g_Vmc_Image[ unit ].header.root_cluster           );
	DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: backup_block1         : 0x%02x\n", g_Vmc_Image[ unit ].header.backup_block1          );
	DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: backup_block2         : 0x%02x\n", g_Vmc_Image[ unit ].header.backup_block2          );

	unsigned int last_blk_sector = 8;
	int i = 0;

	for ( i = 0; i <= (  ( g_Vmc_Image[ unit ].total_pages - 1 ) / 65536 ); i++ ) 
	{
		if (  (  (  ( last_blk_sector + i ) * ( g_Vmc_Image[ unit ].cluster_size / g_Vmc_Image[ unit ].header.page_size )  ) %g_Vmc_Image[ unit ].header.pages_per_block ) == 0 ) 
		{
			last_blk_sector = last_blk_sector + i;
		}
		g_Vmc_Image[ unit ].header.indir_fat_clusters[ i ]= last_blk_sector + i;
	}

	for ( i = 0; g_Vmc_Image[ unit ].header.indir_fat_clusters[ i ]!= 0; i++ ) 
		DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: indir_fat_clusters[%d] : 0x%02x\n", i, g_Vmc_Image[ unit ].header.indir_fat_clusters[ i ]);

	memset ( g_Vmc_Image[ unit ].header.bad_block_list, g_Vmc_Image[ unit ].erase_byte, sizeof ( unsigned int ) * 32 );

	g_Vmc_Image[ unit ].header.unused2 = 0;
	g_Vmc_Image[ unit ].header.unused3 = 0x100;
	g_Vmc_Image[ unit ].header.size_in_megs = ( g_Vmc_Image[ unit ].total_pages * g_Vmc_Image[ unit ].header.page_size ) / ( g_Vmc_Image[ unit ].cluster_size * g_Vmc_Image[ unit ].cluster_size );
	g_Vmc_Image[ unit ].header.unused4 = 0xFFFFFFFF;
	memset ( g_Vmc_Image[ unit ].header.unused5, g_Vmc_Image[ unit ].erase_byte, 12 );
	g_Vmc_Image[ unit ].header.max_used = ( 97 * g_Vmc_Image[ unit ].header.clusters_per_card ) / 100;
	memset ( g_Vmc_Image[ unit ].header.unused6, g_Vmc_Image[ unit ].erase_byte, 8 );
	g_Vmc_Image[ unit ].header.unused7 = 0xFFFFFFFF;

	DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: mc_type               : 0x%02x\n", g_Vmc_Image[ unit ].header.mc_type                );
	DEBUGPRINT ( 4, "vmcfs: SuperBlock Info: mc_flag               : 0x%02x\n", g_Vmc_Image[ unit ].header.mc_flag                );

	g_Vmc_Image[ unit ].last_free_cluster = g_Vmc_Image[ unit ].header.first_allocatable;

	return 1;

}


//----------------------------------------------------------------------------
// Used for timing functions, use this to optimize stuff
//----------------------------------------------------------------------------
#ifdef PROFILING

	iop_sys_clock_t iop_clock_finished;	// Global clock finished data

	void profilerStart ( iop_sys_clock_t* iopclock ) 
	{

		GetSystemTime ( iopclock );

	}

	void profilerEnd ( const char* function, const char* name, iop_sys_clock_t* iopclock1 ) 
	{

		// Make this the first item, so we don't add time that need not be added
		GetSystemTime ( &iop_clock_finished );

		unsigned int sec1, usec1;
		unsigned int sec2, usec2;

		SysClock2USec ( &iop_clock_finished, &sec2, &usec2 );
		SysClock2USec ( iopclock1, &sec1, &usec1 );

		printf ( "vmcfs: Profiler[ %s ]: %s %ld. %ld seconds\n", function, name, sec2 - sec1, ( usec2 - usec1 ) / 1000 );

	}

#endif


//----------------------------------------------------------------------------
// Get date and time from cdvd fonction and put them into a "vmc_datetime" struct pointer.
//----------------------------------------------------------------------------
int getPs2Time ( vmc_datetime* tm ) 
{

	cd_clock_t	cdtime;
	s32         tmp;

	static vmc_datetime timeBuf = {
		0, 0x00, 0x00, 0x0A, 0x01, 0x01, 2008	// used if can not get time... 
	};

	if ( CdReadClock ( &cdtime ) != 0 && cdtime.stat == 0 ) 
	{

		tmp = cdtime.second >> 4;
		timeBuf.sec = ( unsigned int ) (  (  ( tmp << 2 ) + tmp ) << 1 ) + ( cdtime.second & 0x0F );
		tmp = cdtime.minute >> 4;
		timeBuf.min = (  (  ( tmp << 2 ) + tmp ) << 1 ) + ( cdtime.minute & 0x0F );
		tmp = cdtime.hour >> 4;
		timeBuf.hour = (  (  ( tmp << 2 ) + tmp ) << 1 ) + ( cdtime.hour & 0x0F );
// 		timeBuf.hour = ( timeBuf.hour + 4 ) %24;// TEMP FIX ( need to deal with timezones? ) ... aparently not!
		tmp = cdtime.day >> 4;
		timeBuf.day = (  (  ( tmp << 2 ) + tmp ) << 1 ) + ( cdtime.day & 0x0F );
		tmp = cdtime.month >> 4;
		timeBuf.month = (  (  ( tmp << 2 ) + tmp ) << 1 ) + ( cdtime.month & 0x0F );
		tmp = cdtime.year >> 4;
		timeBuf.year = (  (  ( tmp << 2 ) + tmp ) << 1 ) + ( cdtime.year & 0xF ) + 2000;

	}

	memcpy ( tm, &timeBuf, sizeof ( vmc_datetime )  );

	return 0;

}


//----------------------------------------------------------------------------
// XOR table use for Error Correcting Code ( ECC ) calculation.
//----------------------------------------------------------------------------
const unsigned char ECC_XOR_Table[ 256 ]= {
0x00, 0x87, 0x96, 0x11, 0xA5, 0x22, 0x33, 0xB4, 
0xB4, 0x33, 0x22, 0xA5, 0x11, 0x96, 0x87, 0x00, 
0xC3, 0x44, 0x55, 0xD2, 0x66, 0xE1, 0xF0, 0x77, 
0x77, 0xF0, 0xE1, 0x66, 0xD2, 0x55, 0x44, 0xC3, 
0xD2, 0x55, 0x44, 0xC3, 0x77, 0xF0, 0xE1, 0x66, 
0x66, 0xE1, 0xF0, 0x77, 0xC3, 0x44, 0x55, 0xD2, 
0x11, 0x96, 0x87, 0x00, 0xB4, 0x33, 0x22, 0xA5, 
0xA5, 0x22, 0x33, 0xB4, 0x00, 0x87, 0x96, 0x11, 
0xE1, 0x66, 0x77, 0xF0, 0x44, 0xC3, 0xD2, 0x55, 
0x55, 0xD2, 0xC3, 0x44, 0xF0, 0x77, 0x66, 0xE1, 
0x22, 0xA5, 0xB4, 0x33, 0x87, 0x00, 0x11, 0x96, 
0x96, 0x11, 0x00, 0x87, 0x33, 0xB4, 0xA5, 0x22, 
0x33, 0xB4, 0xA5, 0x22, 0x96, 0x11, 0x00, 0x87, 
0x87, 0x00, 0x11, 0x96, 0x22, 0xA5, 0xB4, 0x33, 
0xF0, 0x77, 0x66, 0xE1, 0x55, 0xD2, 0xC3, 0x44, 
0x44, 0xC3, 0xD2, 0x55, 0xE1, 0x66, 0x77, 0xF0, 
0xF0, 0x77, 0x66, 0xE1, 0x55, 0xD2, 0xC3, 0x44, 
0x44, 0xC3, 0xD2, 0x55, 0xE1, 0x66, 0x77, 0xF0, 
0x33, 0xB4, 0xA5, 0x22, 0x96, 0x11, 0x00, 0x87, 
0x87, 0x00, 0x11, 0x96, 0x22, 0xA5, 0xB4, 0x33, 
0x22, 0xA5, 0xB4, 0x33, 0x87, 0x00, 0x11, 0x96, 
0x96, 0x11, 0x00, 0x87, 0x33, 0xB4, 0xA5, 0x22, 
0xE1, 0x66, 0x77, 0xF0, 0x44, 0xC3, 0xD2, 0x55, 
0x55, 0xD2, 0xC3, 0x44, 0xF0, 0x77, 0x66, 0xE1, 
0x11, 0x96, 0x87, 0x00, 0xB4, 0x33, 0x22, 0xA5, 
0xA5, 0x22, 0x33, 0xB4, 0x00, 0x87, 0x96, 0x11, 
0xD2, 0x55, 0x44, 0xC3, 0x77, 0xF0, 0xE1, 0x66, 
0x66, 0xE1, 0xF0, 0x77, 0xC3, 0x44, 0x55, 0xD2, 
0xC3, 0x44, 0x55, 0xD2, 0x66, 0xE1, 0xF0, 0x77, 
0x77, 0xF0, 0xE1, 0x66, 0xD2, 0x55, 0x44, 0xC3, 
0x00, 0x87, 0x96, 0x11, 0xA5, 0x22, 0x33, 0xB4, 
0xB4, 0x33, 0x22, 0xA5, 0x11, 0x96, 0x87, 0x00, 
};


//----------------------------------------------------------------------------
// Calculate ECC for a 128 bytes chunk of data
//----------------------------------------------------------------------------
int calculateECC ( char* ECC_Chunk, const unsigned char* Data_Chunk ) 
{
	int i, c;

	ECC_Chunk[ 0 ]= ECC_Chunk[ 1 ]= ECC_Chunk[ 2 ]= 0;

	for ( i = 0; i < 0x80; i++ ) 
	{

		c = ECC_XOR_Table[ Data_Chunk[ i ] ];

		ECC_Chunk[ 0 ] ^= c;

		if ( c & 0x80 ) 
		{

			ECC_Chunk[ 1 ] ^= ~i;
			ECC_Chunk[ 2 ] ^= i;

		}

	}

	ECC_Chunk[ 0 ] = ~ECC_Chunk[ 0 ];
	ECC_Chunk[ 0 ] &= 0x77;
	ECC_Chunk[ 1 ] = ~ECC_Chunk[ 1 ];
	ECC_Chunk[ 1 ] &= 0x7f;
	ECC_Chunk[ 2 ] = ~ECC_Chunk[ 2 ];
	ECC_Chunk[ 2 ] &= 0x7f;

	return 1;

}


//----------------------------------------------------------------------------
// Build ECC from a complet page of data
//----------------------------------------------------------------------------
int buildECC ( int unit, char* Page_Data, char* ECC_Data ) 
{

	char Data_Chunk[ 4 ][ 128 ];
	char ECC_Chunk[ 4 ][ 3 ];
	char ECC_Pad[ 4 ];

	// This is to divide the page in 128 bytes chunks
	memcpy ( Data_Chunk[ 0 ], Page_Data +   0, 128 );
	memcpy ( Data_Chunk[ 1 ], Page_Data + 128, 128 );
	memcpy ( Data_Chunk[ 2 ], Page_Data + 256, 128 );
	memcpy ( Data_Chunk[ 3 ], Page_Data + 384, 128 );

	// Ask for 128 bytes chunk ECC calculation, it returns 3 bytes per chunk
	calculateECC ( ECC_Chunk[ 0 ], Data_Chunk[ 0 ]);
	calculateECC ( ECC_Chunk[ 1 ], Data_Chunk[ 1 ]);
	calculateECC ( ECC_Chunk[ 2 ], Data_Chunk[ 2 ]);
	calculateECC ( ECC_Chunk[ 3 ], Data_Chunk[ 3 ]);

	// Prepare Padding as ECC took only 12 bytes and stand on 16 bytes
	memset ( ECC_Pad, g_Vmc_Image[ unit ].erase_byte, sizeof ( ECC_Pad )  );

	// "MemCopy" our four 3 bytes ECC chunks into our 16 bytes ECC data buffer
	// Finaly "MemCopy" our 4 bytes PAD chunks into last 4 bytes of our ECC data buffer
	memcpy ( ECC_Data + 0, ECC_Chunk[ 0 ], 3 );
	memcpy ( ECC_Data + 3, ECC_Chunk[ 1 ], 3 );
	memcpy ( ECC_Data + 6, ECC_Chunk[ 2 ], 3 );
	memcpy ( ECC_Data + 9, ECC_Chunk[ 3 ], 3 );
	memcpy ( ECC_Data + 12, ECC_Pad      , 4 );

	return 1;

}
