#include <tamtypes.h>
#include <sifdma.h>
#include "sifrpc.h"
#include <loadcore.h>
#include <thsemap.h>
#include <intrman.h>
#include <thbase.h>
#include <sifcmd.h>
#include <ioman.h>
#include "ps2lib_ioman.h"
#include <sysclib.h>
#include <stdio.h>
#include <sysmem.h>

#include "cdvd_iop.h"

#define TRUE	1
#define FALSE	0

#define TH_C		0x02000000

enum PathMatch
{
	NOT_MATCH = 0,
	MATCH,
	SUBDIR
};

//#define DEBUG

// 16 sectors worth of toc entry
#define MAX_DIR_CACHE_SECTORS 32


//static u8 cdVolDescriptor[2048];
static CdRMode cdReadMode;

int lastsector;
int last_bk=0;


struct rootDirTocHeader
{
	u16	length;
	u32 tocLBA;
	u32 tocLBA_bigend;
	u32 tocSize;
	u32 tocSize_bigend;
	u8	dateStamp[8];
	u8	reserved[6];
	u8	reserved2;
	u8	reserved3;
} __attribute__((packed));

struct asciiDate
{
	char	year[4];
	char	month[2];
	char	day[2];
	char	hours[2];
	char	minutes[2];
	char	seconds[2];
	char	hundreths[2];
	char	terminator[1];
} __attribute__((packed));

struct cdVolDesc
{
	u8		filesystemType;	// 0x01 = ISO9660, 0x02 = Joliet, 0xFF = NULL
	u8		volID[5];		// "CD001"
	u8		reserved2;
	u8		reserved3;
	u8		sysIdName[32];
	u8		volName[32];	// The ISO9660 Volume Name
	u8		reserved5[8];
	u32		volSize;		// Volume Size
	u32		volSizeBig;		// Volume Size Big-Endian
	u8		reserved6[32];
	u32		unknown1;
	u32		unknown1_bigend;
	u16		volDescSize;
	u16		volDescSize_bigend;
	u32		unknown3;
	u32		unknown3_bigend;
	u32		priDirTableLBA;	// LBA of Primary Dir Table
	u32		reserved7;
	u32		secDirTableLBA;	// LBA of Secondary Dir Table
	u32		reserved8;
	struct rootDirTocHeader	rootToc;
	u8		volSetName[128];
	u8		publisherName[128];
	u8		preparerName[128];
	u8		applicationName[128];
	u8		copyrightFileName[37];
	u8		abstractFileName[37];
	u8		bibliographyFileName[37];
	struct	asciiDate	creationDate;
	struct	asciiDate	modificationDate;
	struct	asciiDate	effectiveDate;
	struct	asciiDate	expirationDate;
	u8		reserved10;
	u8		reserved11[1166];
} __attribute__((packed));

struct dirTableEntry
{
	u8	dirNameLength;
	u8	reserved;
	u32	dirTOCLBA;
	u16 dirDepth;
	u8	dirName[32];
} __attribute__((packed));

struct dirTocEntry
{
	short	length;
	unsigned int	fileLBA;
	unsigned int	fileLBA_bigend;
	unsigned int	fileSize;
	unsigned int	fileSize_bigend;
	unsigned char	dateStamp[6];
	unsigned char	reserved1;
	unsigned char	fileProperties;
	unsigned char	reserved2[6];
	unsigned char	filenameLength;
	unsigned char	filename[128];
} __attribute__((packed));	// This is the internal format on the CD
// a file with a single character filename will have a 34byte toc entry
// (max 60 entries per sector)6

// TocEntry structure contains only the important stuff needed for export
//

struct fdtable
{
	int fd;
	int fileSize;
	int LBA;
	int filePos;
};


struct dir_cache_info
{
	char pathname[1024];		// The pathname of the cached directory
	unsigned int valid;			// TRUE if cache data is valid, FALSE if not

	unsigned int path_depth;	// The path depth of the cached directory (0 = root)

	unsigned int sector_start;	// The start sector (LBA) of the cached directory
	unsigned int sector_num;	// The total size of the directory (in sectors)
	unsigned int cache_offset;	// The offset from sector_start of the cached area
	unsigned int cache_size;	// The size of the cached directory area (in sectors)

	char* cache;				// The actual cached data
};


static struct dir_cache_info CachedDirInfo;

enum Cache_getMode {
	CACHE_START = 0,
	CACHE_NEXT = 1
};

static struct cdVolDesc CDVolDesc;

static unsigned int *buffer; // RPC send/receive buffer
struct t_SifRpcDataQueue qd;
struct t_SifRpcServerData sd0;


static struct fdtable fd_table[16];
static int fd_used[16];
static int files_open;

static struct fileio_driver file_driver;

/* Filing-system exported functions */
void CDVD_init( struct fileio_driver *driver);
int CDVD_open( int kernel_fd, char *name, int mode);
int CDVD_lseek(int kernel_fd, int offset, int whence);
int CDVD_read( int kernel_fd, char * buffer, int size );
int CDVD_write( int fd, char * buffer, int size );
int CDVD_close( int kernel_fd);

/* RPC exported functions */
int CDVD_findfile(const char* fname, struct TocEntry* tocEntry);
int CDVD_stop(void);
int CDVD_trayreq(int mode);
int CDVD_diskready(void);
int CDVD_GetDir_RPC(const char* pathname, const char* extensions, enum CDVD_getMode getMode, struct TocEntry tocEntry[], unsigned int req_entries);

int CDVD_getdir_IOP(const char* pathname, const char* extensions, enum CDVD_getMode getMode, struct TocEntry tocEntry[], unsigned int req_entries);


// Functions called by the RPC server
void* CDVDRpc_Stop();
void* CDVDRpc_FlushCache();
void* CDVDRpc_TrayReq(unsigned int* sbuff);
void* CDVDRpc_DiskReady(unsigned int* sbuff);
void* CDVDRpc_FindFile(unsigned int* sbuff);
void* CDVDRpc_Getdir(unsigned int* sbuff);
void* CDVDRpc_GetSize(unsigned int* sbuff);


/* Internal use functions */
int strcasecmp(const char *s1, const char *s2);
int strncasecmp(const char *s1, const char *s2, int limit);
int CDVD_GetVolumeDescriptor(void);
void _splitpath (const char *constpath, char *dir, char *fname);
void TocEntryCopy(struct TocEntry* tocEntry, struct dirTocEntry* internalTocEntry);
int TocEntryCompare(char* filename, const char* extensions);

enum PathMatch ComparePath(const char* path);

int CDVD_Cache_Dir(const char* pathname, enum Cache_getMode getMode);
int FindPath(char* pathname);


void* CDVD_rpc_server(int fno, void *data, int size);
void CDVD_Thread(void* param);



/********************
* Optimised CD Read *
********************/

int ReadSect(u32 lsn, u32 sectors, void *buf, CdRMode *mode)
{
	int retry;
	int result=0;
	cdReadMode.trycount=32;
	
	for(retry=0;retry<32;retry++) // 32 retries
    {
		if(retry <= 8)
			cdReadMode.spindlctrl=1; // Try fast reads for first 8 tries
		else
			cdReadMode.spindlctrl=0; // Then try slow reads
	
		CdDiskReady(0);

		if (CdRead(lsn, sectors, buf, mode) != TRUE)
		{
			// Failed to read
			if(retry==31)
			{
				// Still failed after last retry
				memset(buf,0,(sectors<<11));
				// printf("Couldn't Read from file for some reason\n");
				return FALSE; // error
			}
		}
		else
		{
			// Read okay
			CdSync(0);
			break;
		}
	
		result=CdGetError();
		if(result==0)
			break;

	}

	cdReadMode.trycount=32;
	cdReadMode.spindlctrl =1;

	if(result==0)
		return TRUE;

	memset(buf,0,(sectors<<11));

	return FALSE; // error
}


/*************************************************************
* The functions below are the normal file-system operations, *
* used to provide a standard filesystem interface. There is  *
* no need to export these functions for calling via RPC      *
*************************************************************/
int dummy()
{
	#ifdef DEBUG
		printf("CDVD: dummy function called\n");
	#endif

	return -5;
}

void CDVD_init( struct fileio_driver *driver)
{
	printf("CDVD: CDVD Filesystem v1.15\n");
	printf("by A.Lee (aka Hiryu) & Nicholas Van Veen (aka Sjeep)\n");
	printf("CDVD: Initializing '%s' file driver.\n", driver->device);

	CdInit(0);

	memset(fd_table,0,sizeof(fd_table));
	memset(fd_used,0,16*4);

	return;
}



int CDVD_open( int kernel_fd, char *name, int mode)
{
	int j;
	static struct TocEntry tocEntry;

	#ifdef DEBUG
		printf("CDVD: fd_open called.\n" );
   		printf("      kernel_fd.. %d\n", kernel_fd);
   		printf("      name....... %s %x\n", name, (int)name);
   		printf("      mode....... %d\n\n", mode);
	#endif

	// check if the file exists
	if (CDVD_findfile(name,&tocEntry) != TRUE)
	{
		return -1;
	}

	if(mode != O_RDONLY)
		return -2;

	// set up a new file descriptor
	for(j=0; j < 16; j++)
	{
		if(fd_used[j] == 0)
			break;
	}

	if(j >= 16) return -3;


	fd_used[j] = 1;
	files_open++;

	#ifdef DEBUG
		printf("CDVD: internal fd %d\n", j);
	#endif

	fd_table[j].fd = kernel_fd;
	fd_table[j].fileSize = tocEntry.fileSize;
	fd_table[j].LBA = tocEntry.fileLBA;
	fd_table[j].filePos = 0;

	#ifdef DEBUG
		printf("tocEntry.fileSize = %d\n",tocEntry.fileSize);

		printf("Opened file: %s\n",name);
	#endif

   	return kernel_fd;
}



int CDVD_lseek(int kernel_fd, int offset, int whence)
{
	int i;

	#ifdef DEBUG
		printf("CDVD: fd_seek called.\n");
		printf("      kernel_fd... %d\n", kernel_fd);
		printf("      offset...... %d\n", offset);
		printf("      whence...... %d\n\n", whence);
	#endif

	for(i=0; i < 16; i++)
	{
		if(fd_table[i].fd == kernel_fd)
			break;
	}

	if(i >= 16)
	{
		#ifdef DEBUG
			printf("CDVD_lseek: ERROR: File does not appear to be open!\n");
		#endif

		return -1;
	}

	switch(whence)
	{
	case SEEK_SET:
		fd_table[i].filePos = offset;
		break;

	case SEEK_CUR:
		fd_table[i].filePos += offset;
		break;

	case SEEK_END:
		fd_table[i].filePos = fd_table[i].fileSize + offset;
		break;

	default:
		return -1;
	}

	if (fd_table[i].filePos < 0)
		fd_table[i].filePos = 0;

	if (fd_table[i].filePos > fd_table[i].fileSize)
		fd_table[i].filePos = fd_table[i].fileSize;

	return fd_table[i].filePos;
}


int CDVD_read( int kernel_fd, char * buffer, int size )
{
	int i;

	int start_sector;
	int off_sector;
	int num_sectors;

	int read=0;
//	int sector;

//	int size_left;
//	int copy_size;

	static char local_buffer[9*2048];


	#ifdef DEBUG
   		printf("CDVD: read called\n");
   		printf("      kernel_fd... %d\n",kernel_fd);
   		printf("      buffer...... 0x%X\n",(int)buffer);
   		printf("      size........ %d\n\n",size);
	#endif

	for(i=0; i < 16; i++)
	{
		if(fd_table[i].fd == kernel_fd)
			break;
	}

	if(i >= 16)
	{
		#ifdef DEBUG
			printf("CDVD_read: ERROR: File does not appear to be open!\n");
		#endif

		return -1;
	}


		// A few sanity checks
	if (fd_table[i].filePos > fd_table[i].fileSize)
	{
		// We cant start reading from past the beginning of the file
		return 0;	// File exists but we couldnt read anything from it
	}

	if ((fd_table[i].filePos+size) > fd_table[i].fileSize)
		size = fd_table[i].fileSize - fd_table[i].filePos;

	if (size<=0)
		return 0;

	if (size>16384)
		size=16384; 

	// Now work out where we want to start reading from
	start_sector = fd_table[i].LBA + (fd_table[i].filePos >> 11);
	off_sector = (fd_table[i].filePos & 0x7FF);

	num_sectors = (off_sector + size);
	num_sectors = (num_sectors>>11)+((num_sectors & 2047)!=0);

	#ifdef DEBUG
		printf("CDVD_read: read sectors %d to %d\n",start_sector,start_sector+num_sectors);
	#endif

	// Skip a Sector for equal (use the last sector in buffer)
	if(start_sector==lastsector)
	{
		read=1;
		if (last_bk > 0)
			memcpy(local_buffer,local_buffer+2048*(last_bk),2048);
		last_bk=0;
	}
	
	lastsector=start_sector+num_sectors-1;
	// Read the data (we only ever get 16KB max request at once)

	if(read==0 || (read==1 && num_sectors>1))
	{
		if (ReadSect(start_sector+read, num_sectors-read, local_buffer+((read)<<11), &cdReadMode) != TRUE)
		{
			#ifdef DEBUG
				printf("Couldn't Read from file for some reason\n");
			#endif
		}
	 
		last_bk=num_sectors-1;
	}

	memcpy(buffer,local_buffer+off_sector,size);

	fd_table[i].filePos += size;

	return (size);
}


int CDVD_write( int kernel_fd, char * buffer, int size )
{
   if(size == 0) return 0;
   else 		 return -1;
}



int CDVD_close( int kernel_fd)
{
	int i;

	#ifdef DEBUG
   		printf("CDVD: fd_close called.\n" );
   		printf("      kernel fd.. %d\n\n", kernel_fd);
	#endif

	for(i=0; i < 16; i++)
	{
		if(fd_table[i].fd == kernel_fd)
			break;
	}

	if(i >= 16)
	{
		#ifdef DEBUG
			printf("CDVD_close: ERROR: File does not appear to be open!\n");
		#endif

		return -1;
	}

	#ifdef DEBUG
		printf("CDVD: internal fd %d\n",i);
	#endif

	fd_used[i] = 0;
	files_open--;

    return 0;
}


static void *filedriver_functarray[16];

int _start( int argc, char **argv)
{
	int	i;
	struct _iop_thread param;
	int th;

	// Initialise the directory cache
	strcpy(CachedDirInfo.pathname, "");	// The pathname of the cached directory
	CachedDirInfo.valid = FALSE;			// Cache is not valid
	CachedDirInfo.path_depth = 0;			// 0 = root)
	CachedDirInfo.sector_start = 0;			// The start sector (LBA) of the cached directory
	CachedDirInfo.sector_num = 0;			// The total size of the directory (in sectors)
	CachedDirInfo.cache_offset = 0;			// The offset from sector_start of the cached area
	CachedDirInfo.cache_size = 0;			// The size of the cached directory area (in sectors)

	if (CachedDirInfo.cache == NULL)
		CachedDirInfo.cache = (char*)AllocSysMemory(0,MAX_DIR_CACHE_SECTORS*2048,NULL);


	// setup the cdReadMode structure
	cdReadMode.trycount = 0;
	cdReadMode.spindlctrl = CdSpinStm;
	cdReadMode.datapattern = CdSecS2048;

	// setup the file_driver structure
	file_driver.device = "cdfs";
	file_driver.xx1 = 16;
	file_driver.version = 1;
	file_driver.description = "CDVD Filedriver";
	file_driver.function_list = filedriver_functarray;

	for (i=0;i < 16; i++)
		filedriver_functarray[i] = dummy;

	filedriver_functarray[ FIO_INITIALIZE ] = CDVD_init;
	filedriver_functarray[ FIO_OPEN ] = CDVD_open;
	filedriver_functarray[ FIO_CLOSE ] = CDVD_close;
	filedriver_functarray[ FIO_READ ] = CDVD_read;
	filedriver_functarray[ FIO_WRITE ] = CDVD_write;
	filedriver_functarray[ FIO_SEEK ] = CDVD_lseek;

	FILEIO_del( "cdfs");
	FILEIO_add( &file_driver);

	param.attr         = TH_C;
	param.thread       = (void*)CDVD_Thread;
	param.priority 	  = 40;
	param.stacksize    = 0x8000;
	param.option       = 0;

	th = CreateThread(&param);

	if (th > 0)
	{
		StartThread(th,0);
		return 0;
	}
	else
		return 1;

	return 0;
}

/**************************************************************
* The functions below are not exported for normal file-system *
* operations, but are used by the file-system operations, and *
* may also be exported  for use via RPC                       *
**************************************************************/


int CDVD_GetVolumeDescriptor(void)
{
	// Read until we find the last valid Volume Descriptor
	int volDescSector;

	static struct cdVolDesc localVolDesc;

	#ifdef DEBUG
		printf("CDVD_GetVolumeDescriptor called\n");
	#endif

	for (volDescSector = 16; volDescSector<20; volDescSector++)
	{
		ReadSect(volDescSector,1,&localVolDesc,&cdReadMode);

		// If this is still a volume Descriptor
		if (strncmp(localVolDesc.volID, "CD001", 5) == 0)
		{
			if ((localVolDesc.filesystemType == 1) ||
				(localVolDesc.filesystemType == 2))
			{
				memcpy(&CDVolDesc, &localVolDesc, sizeof(struct cdVolDesc));
			}
		}
		else
			break;
	}

#ifdef DEBUG
	switch (CDVolDesc.filesystemType)
	{
	case 1:
		printf("CD FileSystem is ISO9660\n");
		break;

	case 2:
		printf("CD FileSystem is Joliet\n");
		break;

	default:
		printf("CD FileSystem is unknown type\n");
		break;
	}
#endif
//	CdStop();

	return TRUE;
}


int CDVD_findfile(const char* fname, struct TocEntry* tocEntry)
{
	static char filename[128+1];
	static char pathname[1024+1];

	struct dirTocEntry* tocEntryPointer;

	#ifdef DEBUG
		printf("CDVD_findfile called\n");
	#endif

	_splitpath(fname, pathname, filename);

	#ifdef DEBUG
		printf("Trying to find file: %s in directory: %s\n",filename, pathname);
	#endif

//	if ((CachedDirInfo.valid==TRUE)
//		&& (strcasecmp(pathname, CachedDirInfo.pathname)==0))

	if ((CachedDirInfo.valid==TRUE)
		&& (ComparePath(pathname)==MATCH))
	{
		// the directory is already cached, so check through the currently
		// cached chunk of the directory first

		(char*)tocEntryPointer=CachedDirInfo.cache;

		for ( ; (char*)tocEntryPointer < (CachedDirInfo.cache + (CachedDirInfo.cache_size * 2048));	(char*)tocEntryPointer += tocEntryPointer->length)
		{
			if (tocEntryPointer->length == 0)
			{
				#ifdef DEBUG
					printf("Got a null pointer entry, so either reached end of dir, or end of sector\n");
				#endif

				(char*)tocEntryPointer = CachedDirInfo.cache + (((((char*)tocEntryPointer - CachedDirInfo.cache)/2048)+1)*2048);
			}

			if ((char*)tocEntryPointer >= (CachedDirInfo.cache + (CachedDirInfo.cache_size * 2048)))
			{
				// reached the end of the cache block
				break;
			}

			if ((tocEntryPointer->fileProperties & 0x02)==0)
			{
				// It's a file
				TocEntryCopy(tocEntry, tocEntryPointer);

				if (strcasecmp(tocEntry->filename, filename)==0)
				{
					// and it matches !!
					return TRUE;
				}
			}
		} // end of for loop



		// If that was the only dir block, and we havent found it, then fail
		if (CachedDirInfo.cache_size == CachedDirInfo.sector_num)
			return FALSE;

		// Otherwise there is more dir to check
		if (CachedDirInfo.cache_offset == 0)
		{
			// If that was the first block then continue with the next block
			if (CDVD_Cache_Dir(pathname,CACHE_NEXT) != TRUE)
				return FALSE;
		}
		else
		{
			// otherwise (if that wasnt the first block) then start checking from the start 
			if (CDVD_Cache_Dir(pathname,CACHE_START) != TRUE)
				return FALSE;
		}
	}
	else
	{
		#ifdef DEBUG
			printf("Trying to cache directory\n");
		#endif
		// The wanted directory wasnt already cached, so cache it now
		if (CDVD_Cache_Dir(pathname, CACHE_START) != TRUE)
		{
			#ifdef DEBUG
				printf("Failed to cache directory\n");
			#endif

			return FALSE;
		}
	}

	// If we've got here, then we have a block of the directory cached, and want to check
	// from this point, to the end of the dir
	#ifdef DEBUG
		printf("cache_size = %d\n",CachedDirInfo.cache_size);
	#endif

	while (CachedDirInfo.cache_size > 0)
	{
		(char*)tocEntryPointer=CachedDirInfo.cache;
		
		if (CachedDirInfo.cache_offset == 0)
			(char*)tocEntryPointer += tocEntryPointer->length;

		for ( ; (char*)tocEntryPointer < (CachedDirInfo.cache + (CachedDirInfo.cache_size * 2048));	(char*)tocEntryPointer += tocEntryPointer->length)
		{
			if (tocEntryPointer->length == 0)
			{
				#ifdef DEBUG
					printf("Got a null pointer entry, so either reached end of dir, or end of sector\n");
					printf("Offset into cache = %d bytes\n",(char*)tocEntryPointer - CachedDirInfo.cache);
				#endif

				(char*)tocEntryPointer = CachedDirInfo.cache + (((((char*)tocEntryPointer - CachedDirInfo.cache)/2048)+1)*2048);
			}

			if ((char*)tocEntryPointer >= (CachedDirInfo.cache + (CachedDirInfo.cache_size * 2048)))
			{
				// reached the end of the cache block
				break;
			}

			TocEntryCopy(tocEntry, tocEntryPointer);

			if (strcasecmp(tocEntry->filename, filename)==0)
			{
				#ifdef DEBUG
					printf("Found a matching file\n");
				#endif
				// and it matches !!
				return TRUE;
			}

			#ifdef DEBUG
				printf("Non-matching file - looking for %s , found %s\n",filename, tocEntry->filename);
			#endif
		} // end of for loop
		
		#ifdef DEBUG
			printf("Reached end of cache block\n");
		#endif
		// cache the next block
		CDVD_Cache_Dir(pathname, CACHE_NEXT);
	}

	// we've run out of dir blocks to cache, and still not found it, so fail

	#ifdef DEBUG
		printf("CDVD_findfile: could not find file\n");
	#endif

	return FALSE;
}



// Find, and cache, the requested directory, for use by GetDir or  (and thus open)
// provide an optional offset variable, for use when caching dirs of greater than 500 files

// returns TRUE if all TOC entries have been retrieved, or
// returns FALSE if there are more TOC entries to be retrieved
int CDVD_Cache_Dir(const char* pathname, enum Cache_getMode getMode)
{

	// macke sure that the requested pathname is not directly modified
	static char dirname[1024];

	int path_len;

	#ifdef DEBUG
		printf("Attempting to find, and cache, directory: %s\n",pathname);
	#endif

	// only take any notice of the existing cache, if it's valid
	if (CachedDirInfo.valid == TRUE)
	{
	// Check if the requested path is already cached
//		if (strcasecmp(pathname,CachedDirInfo.pathname)==0)
		if (ComparePath(pathname)==MATCH)
		{
			#ifdef DEBUG
				printf("CacheDir: The requested path is already cached\n");
			#endif

			// If so, is the request ot cache the start of the directory, or to resume the next block ?
			if (getMode == CACHE_START)
			{
				#ifdef DEBUG
					printf("          and requested cache from start of dir\n");
				#endif

				if (CachedDirInfo.cache_offset == 0)
				{
					// requested cache of start of the directory, and thats what's already cached
					// so sit back and do nothing
					#ifdef DEBUG
						printf("          and start of dir is already cached so nothing to do :o)\n");
					#endif
					
					CachedDirInfo.valid = TRUE;
					return TRUE;
				}
				else
				{
					// Requested cache of start of the directory, but thats not what's cached
					// so re-cache the start of the directory

					#ifdef DEBUG
						printf("          but dir isn't cached from start, so re-cache existing dir from start\n");
					#endif

					// reset cache data to start of existing directory
					CachedDirInfo.cache_offset = 0;
					CachedDirInfo.cache_size = CachedDirInfo.sector_num;

					if (CachedDirInfo.cache_size > MAX_DIR_CACHE_SECTORS)
						CachedDirInfo.cache_size = MAX_DIR_CACHE_SECTORS;

					// Now fill the cache with the specified sectors
					if (ReadSect(CachedDirInfo.sector_start+CachedDirInfo.cache_offset, CachedDirInfo.cache_size, CachedDirInfo.cache, &cdReadMode) != TRUE)
					{
						#ifdef DEBUG
							printf("Couldn't Read from CD !\n");
						#endif

						CachedDirInfo.valid = FALSE;	// should we completely invalidate just because we couldnt read first time?
						return FALSE;
					}

					CachedDirInfo.valid = TRUE;
					return TRUE;
				}
			} 
			else // getMode == CACHE_NEXT
			{
				// So get the next block of the existing directory

				CachedDirInfo.cache_offset += CachedDirInfo.cache_size;

				CachedDirInfo.cache_size = CachedDirInfo.sector_num - CachedDirInfo.cache_offset;

				if (CachedDirInfo.cache_size > MAX_DIR_CACHE_SECTORS)
					CachedDirInfo.cache_size = MAX_DIR_CACHE_SECTORS;

				// Now fill the cache with the specified sectors
				if (ReadSect(CachedDirInfo.sector_start+CachedDirInfo.cache_offset, CachedDirInfo.cache_size, CachedDirInfo.cache, &cdReadMode) != TRUE)
				{
					#ifdef DEBUG
						printf("Couldn't Read from CD !\n");
					#endif

					CachedDirInfo.valid = FALSE;	// should we completely invalidate just because we couldnt read first time?
					return FALSE;
				}

				CachedDirInfo.valid = TRUE;
				return TRUE;
			}
		} 
		else // requested directory is not the cached directory (but cache is still valid)
		{
			#ifdef DEBUG
				printf("Cache is valid, but cached directory, is not the requested one\n"
					"so check if the requested directory is a sub-dir of the cached one\n");

				printf("Requested Path = %s , Cached Path = %s\n",pathname, CachedDirInfo.pathname);
			#endif


			// Is the requested pathname a sub-directory of the current-directory ?

			// if the requested pathname is longer than the pathname of the cached dir
			// and the pathname of the cached dir matches the beginning of the requested pathname
			// and the next character in the requested pathname is a dir seperator
//			printf("Length of Cached pathname = %d, length of req'd pathname = %d\n",path_len, strlen(pathname));
//			printf("Result of strncasecmp = %d\n",strncasecmp(pathname, CachedDirInfo.pathname, path_len));
//			printf("next character after length of cached name = %c\n",pathname[path_len]);

//			if ((strlen(pathname) > path_len)
//				&& (strncasecmp(pathname, CachedDirInfo.pathname, path_len)==0)	
//				&& ((pathname[path_len]=='/') || (pathname[path_len]=='\\')))

			if (ComparePath(pathname)==SUBDIR)
			{
				// If so then we can start our search for the path, from the currently cached directory
				#ifdef DEBUG
					printf("Requested dir is a sub-dir of the cached directory,\n"
						"so start search from current cached dir\n");
				#endif
				// if the cached chunk, is not the start of the dir, 
				// then we will need to re-load it before starting search
				if (CachedDirInfo.cache_offset != 0)
				{
					CachedDirInfo.cache_offset = 0;
					CachedDirInfo.cache_size = CachedDirInfo.sector_num;
					if (CachedDirInfo.cache_size > MAX_DIR_CACHE_SECTORS)
						CachedDirInfo.cache_size = MAX_DIR_CACHE_SECTORS;

					// Now fill the cache with the specified sectors
					if (ReadSect(CachedDirInfo.sector_start+CachedDirInfo.cache_offset, CachedDirInfo.cache_size, CachedDirInfo.cache, &cdReadMode) != TRUE)
					{
						#ifdef DEBUG
							printf("Couldn't Read from CD !\n");
						#endif

						CachedDirInfo.valid = FALSE;	// should we completely invalidate just because we couldnt read time?
						return FALSE;
					}
				}

				// start the search, with the path after the current directory
				path_len = strlen(CachedDirInfo.pathname);
				strcpy(dirname,pathname+path_len);

				// FindPath should use the current directory cache to start it's search
				// and should change CachedDirInfo.pathname, to the path of the dir it finds
				// it should also cache the first chunk of directory sectors,
				// and fill the contents of the other elements of CachedDirInfo appropriately

				return (FindPath(dirname));
			}
		}
	}

	// If we've got here, then either the cache was not valid to start with
	// or the requested path is not a subdirectory of the currently cached directory
	// so lets start again
	#ifdef DEBUG
		printf("The cache is not valid, or the requested directory is not a sub-dir of the cached one\n");
	#endif

	CdDiskReady(0);

	// Read the main volume descriptor
	if (CDVD_GetVolumeDescriptor() != TRUE)
	{
		#ifdef DEBUG
			printf("Could not read the CD/DVD Volume Descriptor\n");
		#endif

		return -1;
	}

	#ifdef DEBUG
		printf("Read the CD Volume Descriptor\n");
	#endif

	CachedDirInfo.path_depth=0;

	strcpy(CachedDirInfo.pathname,"");

	// Setup the lba and sector size, for retrieving the root toc
	CachedDirInfo.cache_offset =0;
	CachedDirInfo.sector_start = CDVolDesc.rootToc.tocLBA;
	CachedDirInfo.sector_num = (CDVolDesc.rootToc.tocSize>>11) + ((CDVolDesc.rootToc.tocSize & 2047)!=0);

    CachedDirInfo.cache_size = CachedDirInfo.sector_num;

	if (CachedDirInfo.cache_size > MAX_DIR_CACHE_SECTORS)
		CachedDirInfo.cache_size = MAX_DIR_CACHE_SECTORS;


	// Now fill the cache with the specified sectors
	if (ReadSect(CachedDirInfo.sector_start+CachedDirInfo.cache_offset, CachedDirInfo.cache_size, CachedDirInfo.cache, &cdReadMode) != TRUE)
	{
		#ifdef DEBUG
			printf("Couldn't Read from CD !\n");
		#endif

		CachedDirInfo.valid = FALSE;	// should we completely invalidate just because we couldnt read time?
		return FALSE;
	}

	#ifdef DEBUG
		printf("Read the first block from the root directory\n");
	#endif

	// FindPath should use the current directory cache to start it's search (in this case the root)
	// and should change CachedDirInfo.pathname, to the path of the dir it finds
	// it should also cache the first chunk of directory sectors,
	// and fill the contents of the other elements of CachedDirInfo appropriately
	#ifdef DEBUG
		printf("Calling FindPath\n");
	#endif
	strcpy(dirname, pathname);

	return (FindPath(dirname));
}

int FindPath(char* pathname)
{
	char* dirname;
	char* seperator;

	int dir_entry;
	int found_dir;

	struct dirTocEntry* tocEntryPointer;
	struct TocEntry localTocEntry;

	dirname=strtok(pathname,"\\/");

	#ifdef DEBUG
		printf("FindPath: trying to find directory %s\n",pathname);
	#endif

	CdDiskReady(0);

	while(dirname != NULL)
	{
		found_dir=FALSE;

		(char*)tocEntryPointer = CachedDirInfo.cache;
		
		// Always skip the first entry (self-refencing entry)
		(char*)tocEntryPointer += tocEntryPointer->length;

		dir_entry=0;

		for ( ; (char*)tocEntryPointer < (CachedDirInfo.cache + (CachedDirInfo.cache_size * 2048));	(char*)tocEntryPointer += tocEntryPointer->length)
		{
			// If we have a null toc entry, then we've either reached the end of the dir, or have reached a sector boundary
			if (tocEntryPointer->length == 0)
			{
				#ifdef DEBUG
					printf("Got a null pointer entry, so either reached end of dir, or end of sector\n");
				#endif

				(char*)tocEntryPointer = CachedDirInfo.cache + (((((char*)tocEntryPointer - CachedDirInfo.cache)/2048)+1)*2048);
			}

			if ((char*)tocEntryPointer >= (CachedDirInfo.cache + (CachedDirInfo.cache_size * 2048)))
			{
				// If we've gone past the end of the cache
				// then check if there are more sectors to load into the cache

				if ((CachedDirInfo.cache_offset + CachedDirInfo.cache_size) < CachedDirInfo.sector_num)
				{
					// If there are more sectors to load, then load them
					CachedDirInfo.cache_offset += CachedDirInfo.cache_size;
					CachedDirInfo.cache_size = CachedDirInfo.sector_num - CachedDirInfo.cache_offset;

					if (CachedDirInfo.cache_size > MAX_DIR_CACHE_SECTORS)
						CachedDirInfo.cache_size = MAX_DIR_CACHE_SECTORS;

					if (ReadSect(CachedDirInfo.sector_start+CachedDirInfo.cache_offset, CachedDirInfo.cache_size, CachedDirInfo.cache, &cdReadMode) != TRUE)
					{
						#ifdef DEBUG
							printf("Couldn't Read from CD !\n");
						#endif

						CachedDirInfo.valid = FALSE;	// should we completely invalidate just because we couldnt read time?
						return FALSE;
					}

					(char*)tocEntryPointer = CachedDirInfo.cache;
				}
				else
				{
					CachedDirInfo.valid = FALSE;
					return FALSE;
				}
			}

			// If the toc Entry is a directory ...
			if (tocEntryPointer->fileProperties & 0x02)
			{
				// Convert to our format (inc ascii name), for the check
				TocEntryCopy(&localTocEntry, tocEntryPointer);

				// If it's the link to the parent directory, then give it the name ".."
				if (dir_entry == 0)
				{
					if (CachedDirInfo.path_depth != 0)
					{
						#ifdef DEBUG
							printf("First directory entry in dir, so name it '..'\n");
						#endif

						strcpy(localTocEntry.filename, "..");
					}
				}

				// Check if this is the directory that we are looking for
				if (strcasecmp(dirname, localTocEntry.filename) == 0)
				{
					#ifdef DEBUG
						printf("Found the matching sub-directory\n");
					#endif

					found_dir = TRUE;

					if (dir_entry == 0)
					{
						// We've matched with the parent directory
						// so truncate the pathname by one level

						if (CachedDirInfo.path_depth > 0)
							CachedDirInfo.path_depth --;
						
						if (CachedDirInfo.path_depth == 0)
						{
							// If at root then just clear the path to root
							// (simpler than finding the colon seperator etc)
							CachedDirInfo.pathname[0]=0;
						}
						else
						{
							seperator = strrchr(CachedDirInfo.pathname, '/');

							if (seperator != NULL)
								*seperator = 0;
						}
					}
					else
					{
						// otherwise append a seperator, and the matched directory
						// to the pathname
						strcat(CachedDirInfo.pathname, "/");

						#ifdef DEBUG
							printf("Adding '%s' to cached pathname - path depth = %d\n",dirname,CachedDirInfo.path_depth);
						#endif

						strcat(CachedDirInfo.pathname, dirname);

						CachedDirInfo.path_depth ++;
					}

					// Exit out of the search loop
					// (and find the next sub-directory, if there is one)
					break;
				}
				else
				{
					#ifdef DEBUG
						printf("Found a directory, but it doesn't match\n");
					#endif
				}
			}

			dir_entry++;

		} // end of cache block search loop

		
		// if we've reached here, without finding the directory, then it's not there
		if (found_dir != TRUE)
		{
			CachedDirInfo.valid = FALSE;
			return FALSE;
		}

		// find name of next dir
		dirname = strtok(NULL,"\\/");

		CachedDirInfo.sector_start = localTocEntry.fileLBA;
		CachedDirInfo.sector_num = (localTocEntry.fileSize >> 11) + ((CDVolDesc.rootToc.tocSize & 2047)!=0);

		// Cache the start of the found directory
		// (used in searching if this isn't the last dir,
		// or used by whatever requested the cache in the first place if it is the last dir)
		CachedDirInfo.cache_offset = 0;
		CachedDirInfo.cache_size = CachedDirInfo.sector_num;

		if (CachedDirInfo.cache_size > MAX_DIR_CACHE_SECTORS)
			CachedDirInfo.cache_size = MAX_DIR_CACHE_SECTORS;

		if (ReadSect(CachedDirInfo.sector_start+CachedDirInfo.cache_offset, CachedDirInfo.cache_size, CachedDirInfo.cache, &cdReadMode) != TRUE)
		{
			#ifdef DEBUG
				printf("Couldn't Read from CD, trying to read %d sectors, starting at sector %d !\n",
					CachedDirInfo.cache_size, CachedDirInfo.sector_start+CachedDirInfo.cache_offset);
			#endif

			CachedDirInfo.valid = FALSE;	// should we completely invalidate just because we couldnt read time?
			return FALSE;
		}
	}

	// If we've got here then we found the requested directory
	#ifdef DEBUG
		printf("FindPath found the path\n");
	#endif

	CachedDirInfo.valid = TRUE;
	return TRUE;
}



// This is the getdir for use by IOP clients
// fills an array of TocEntry stucts in IOP memory
int CDVD_getdir_IOP(const char* pathname, const char* extensions, enum CDVD_getMode getMode, struct TocEntry tocEntry[], unsigned int req_entries)
{
	// TO DO
	return FALSE;
}


// This is the getdir for use by the EE RPC client
// It DMA's entries to the specified buffer in EE memory
int CDVD_GetDir_RPC(const char* pathname, const char* extensions, enum CDVD_getMode getMode, struct TocEntry tocEntry[], unsigned int req_entries)
{
	int matched_entries;
	int dir_entry;

	struct TocEntry localTocEntry;
	
	struct dirTocEntry* tocEntryPointer;

	int intStatus;	// interrupt status - for dis/en-abling interrupts

	struct t_SifDmaTransfer dmaStruct;
	int dmaID;

	dmaID = 0;

	#ifdef DEBUG
		printf("RPC GetDir Request\n");
	#endif

	matched_entries = 0;

	// pre-cache the dir (and get the new pathname - in-case selected "..")
	if (CDVD_Cache_Dir(pathname, CACHE_START) != TRUE)
	{
		#ifdef DEBUG
			printf("CDVD_GetDir_RPC - Call of CDVD_Cache_Dir failed\n");
		#endif

		return -1;
	}

	#ifdef DEBUG
		printf("requested directory is %d sectors\n",CachedDirInfo.sector_num);
	#endif

	if ((getMode == CDVD_GET_DIRS_ONLY) || (getMode == CDVD_GET_FILES_AND_DIRS))
	{
		// Cache the start of the requested directory
		if (CDVD_Cache_Dir(CachedDirInfo.pathname, CACHE_START) != TRUE)
		{
			#ifdef DEBUG
				printf("CDVD_GetDir_RPC - Call of CDVD_Cache_Dir failed\n");
			#endif

			return -1;
		}

		(char*)tocEntryPointer = CachedDirInfo.cache;

		// skip the first self-referencing entry
		(char*)tocEntryPointer += tocEntryPointer->length;

		// skip the parent entry if this is the root
		if (CachedDirInfo.path_depth == 0)
			(char*)tocEntryPointer += tocEntryPointer->length;

		dir_entry = 0;

		while(1)
		{
			#ifdef DEBUG
				printf("CDVD_GetDir_RPC - inside while-loop\n");
			#endif

			// parse the current cache block
			for( ; (char*)tocEntryPointer < (CachedDirInfo.cache + (CachedDirInfo.cache_size * 2048));	(char*)tocEntryPointer += tocEntryPointer->length)
			{
				if (tocEntryPointer->length == 0)
				{
					// if we have a toc entry length of zero,
					// then we've either reached the end of the sector, or the end of the dir
					// so point to next sector (if there is one - will be checked by next condition)

					(char*)tocEntryPointer = CachedDirInfo.cache + (((((char*)tocEntryPointer - CachedDirInfo.cache)/2048)+1)*2048);
				}

				if ((char*)tocEntryPointer >= CachedDirInfo.cache + (CachedDirInfo.cache_size * 2048))
				{
					// we've reached the end of the current cache block (which may be end of entire dir
					// so just break the loop
					break;
				}

				// Check if the current entry is a dir or a file
				if (tocEntryPointer->fileProperties & 0x02)
				{
					#ifdef DEBUG
						printf("We found a dir, and we want all dirs\n");
					#endif

					// wait for any previous DMA to complete
					// before over-writing localTocEntry
					while(sceSifDmaStat(dmaID)>=0);

					TocEntryCopy(&localTocEntry, tocEntryPointer);

					if (dir_entry==0)
					{
						if (CachedDirInfo.path_depth != 0)
						{
							#ifdef DEBUG
								printf("It's the first directory entry, so name it '..'\n");
							#endif

							strcpy(localTocEntry.filename, "..");
						}
					}

					// DMA localTocEntry to the address specified by tocEntry[matched_entries]
					
					// setup the dma struct
					dmaStruct.src = &localTocEntry;
					dmaStruct.dest = &tocEntry[matched_entries];
					dmaStruct.size = sizeof(struct TocEntry);
					dmaStruct.attr = 0;

					// Do the DMA transfer
					CpuSuspendIntr(&intStatus);

					dmaID = sceSifSetDma(&dmaStruct, 1);

					CpuResumeIntr(intStatus);

					matched_entries++;
				}
				else // it must be a file
				{
					#ifdef DEBUG
						printf("We found a file, but we dont want files (at least not yet)\n");
					#endif
				}

				dir_entry++;

				if (matched_entries >= req_entries) // if we've filled the requested buffer
					return (matched_entries);	// then just return

			} // end of the current cache block

			// if there is more dir to load, then load next chunk, else finish
			if ((CachedDirInfo.cache_offset+CachedDirInfo.cache_size) < CachedDirInfo.sector_num)
			{
				if (CDVD_Cache_Dir(CachedDirInfo.pathname, CACHE_NEXT) != TRUE)
				{
					// failed to cache next block (should return TRUE even if
					// there is no more directory, as long as a CD read didnt fail
					return -1;
				}
			}
			else
				break;

			(char*)tocEntryPointer = CachedDirInfo.cache;
		}
	}

	// Next do files
	if ((getMode == CDVD_GET_FILES_ONLY) || (getMode == CDVD_GET_FILES_AND_DIRS))
	{
		// Cache the start of the requested directory
		if (CDVD_Cache_Dir(CachedDirInfo.pathname, CACHE_START) != TRUE)
		{
			#ifdef DEBUG
				printf("CDVD_GetDir_RPC - Call of CDVD_Cache_Dir failed\n");
			#endif

			return -1;
		}

		(char*)tocEntryPointer = CachedDirInfo.cache;

		// skip the first self-referencing entry
		(char*)tocEntryPointer += tocEntryPointer->length;

		// skip the parent entry if this is the root
		if (CachedDirInfo.path_depth == 0)
			(char*)tocEntryPointer += tocEntryPointer->length;

		dir_entry = 0;

		while(1)
		{
			#ifdef DEBUG
				printf("CDVD_GetDir_RPC - inside while-loop\n");
			#endif

			// parse the current cache block
			for( ; (char*)tocEntryPointer < (CachedDirInfo.cache + (CachedDirInfo.cache_size * 2048));	(char*)tocEntryPointer += tocEntryPointer->length)
			{
				if (tocEntryPointer->length == 0)
				{
					// if we have a toc entry length of zero,
					// then we've either reached the end of the sector, or the end of the dir
					// so point to next sector (if there is one - will be checked by next condition)

					(char*)tocEntryPointer = CachedDirInfo.cache + (((((char*)tocEntryPointer - CachedDirInfo.cache)/2048)+1)*2048);
				}

				if ((char*)tocEntryPointer >= CachedDirInfo.cache + (CachedDirInfo.cache_size * 2048))
				{
					// we've reached the end of the current cache block (which may be end of entire dir
					// so just break the loop
					break;
				}

				// Check if the current entry is a dir or a file
				if (tocEntryPointer->fileProperties & 0x02)
				{
					#ifdef DEBUG
						printf("We don't want files now\n");
					#endif
				}
				else // it must be a file
				{
					// wait for any previous DMA to complete
					// before over-writing localTocEntry
					while(sceSifDmaStat(dmaID)>=0);

					TocEntryCopy(&localTocEntry, tocEntryPointer);

					if (strlen(extensions) > 0)
					{
						// check if the file matches the extension list
						if (TocEntryCompare(localTocEntry.filename, extensions) == TRUE)
						{
							#ifdef DEBUG
								printf("We found a file that matches the requested extension list\n");
							#endif

							// DMA localTocEntry to the address specified by tocEntry[matched_entries]
							
							// setup the dma struct
							dmaStruct.src = &localTocEntry;
							dmaStruct.dest = &tocEntry[matched_entries];
							dmaStruct.size = sizeof(struct TocEntry);
							dmaStruct.attr = 0;

							// Do the DMA transfer
							CpuSuspendIntr(&intStatus);

							dmaID = sceSifSetDma(&dmaStruct, 1);

							CpuResumeIntr(intStatus);

							matched_entries++;
						}
						else
						{
							#ifdef DEBUG
								printf("We found a file, but it didnt match the requested extension list\n");
							#endif
						}
					}
					else // no extension list to match against
					{
						#ifdef DEBUG
							printf("We found a file, and there is not extension list to match against\n");
						#endif

						// DMA localTocEntry to the address specified by tocEntry[matched_entries]
						
						// setup the dma struct
						dmaStruct.src = &localTocEntry;
						dmaStruct.dest = &tocEntry[matched_entries];
						dmaStruct.size = sizeof(struct TocEntry);
						dmaStruct.attr = 0;

						// Do the DMA transfer
						CpuSuspendIntr(&intStatus);

						dmaID = sceSifSetDma(&dmaStruct, 1);

						CpuResumeIntr(intStatus);

						matched_entries++;
					}
				}

				dir_entry++;

				if (matched_entries >= req_entries) // if we've filled the requested buffer
					return (matched_entries);	// then just return

			} // end of the current cache block

			
			// if there is more dir to load, then load next chunk, else finish
			if ((CachedDirInfo.cache_offset+CachedDirInfo.cache_size) < CachedDirInfo.sector_num)
			{
				if (CDVD_Cache_Dir(CachedDirInfo.pathname, CACHE_NEXT) != TRUE)
				{
					// failed to cache next block (should return TRUE even if
					// there is no more directory, as long as a CD read didnt fail
					return -1;
				}
			}
			else
				break;

			(char*)tocEntryPointer = CachedDirInfo.cache;
		}
	}
	// reached the end of the dir, before filling up the requested entries

	return (matched_entries);
}

int CdFlushCache(void)
{
	strcpy(CachedDirInfo.pathname, "");	// The pathname of the cached directory
	CachedDirInfo.valid = FALSE;			// Cache is not valid
	CachedDirInfo.path_depth = 0;			// 0 = root)
	CachedDirInfo.sector_start = 0;			// The start sector (LBA) of the cached directory
	CachedDirInfo.sector_num = 0;			// The total size of the directory (in sectors)
	CachedDirInfo.cache_offset = 0;			// The offset from sector_start of the cached area
	CachedDirInfo.cache_size = 0;			// The size of the cached directory area (in sectors)

	return TRUE;
}

unsigned int CdGetSize(void)
{
	if (CDVD_GetVolumeDescriptor() != TRUE)
		return TRUE;

	return CDVolDesc.volSize;

}

void* CDVDRpc_FlushCache()
{
	CdFlushCache();

	return NULL;
}


void* CDVDRpc_Stop()
{

	CdStop();

	return NULL;

}

// Send:   Offset 0 = mode. Size = int
// Return: Offset 0 = traycnt. Size = int
void* CDVDRpc_TrayReq(unsigned int* sbuff)
{
	int ret;

	CdTrayReq(sbuff[0],(s32*)&ret);

	sbuff[0] = ret;
	return sbuff;
}

// Send:   Offset 0 = mode
// Return: Offset 0 = ret val (cd status)
void* CDVDRpc_DiskReady(unsigned int* sbuff)
{
	int ret;

	ret = CdDiskReady(sbuff[0]);

	sbuff[0] = ret;
	return sbuff;
}

// Send:   Offset 0 = filename string (1024 bytes)
// Return: Offset 0 = ret val (true/false). Size = int
//         Offset 1024 = start of TocEntry structure
void* CDVDRpc_FindFile(unsigned int* sbuff)
{
	int ret;

	ret = CDVD_findfile((char*)&sbuff[0],(struct TocEntry*)&sbuff[1024/4]);

	sbuff[0] = ret;

	return sbuff;
}

// Send:   Offset 0 = filename string (1024 bytes)
// Send:   Offset 1024 = extension string (128 bytes)
// Send:   Offset 1152 = CDVD_getMode
// Send:   Offset 1156 = pointer to array of TocEntry structures in EE mem
// Send:   Offset 1160 = requested number of entries

// Return: Offset 0 = ret val (number of matched entries). Size = int
// Return: Offset 4 = updated pathname (for if path selected = ".."
void* CDVDRpc_Getdir(unsigned int* sbuff)
{
	int ret;

	ret=CDVD_GetDir_RPC(
		(char*)&sbuff[0/4],		// pathname string
		(char*)&sbuff[1024/4],	// extension string
		sbuff[1152/4],			// CDVD_getMode
		(struct TocEntry*) sbuff[1156/4],	// pointer to array of TocEntry structures in EE mem
		sbuff[1160/4]			// requested number of entries
		);

	sbuff[0] = ret;
	strcpy((char*)&sbuff[1],CachedDirInfo.pathname);
	return sbuff;
}

void* CDVDRpc_GetSize(unsigned int* sbuff)
{
	sbuff[0] = CdGetSize();
	return sbuff;
}

/*************************************************
* The functions below are for internal use only, *
* and are not to be exported                     *
*************************************************/

void CDVD_Thread(void* param)
{
	#ifdef DEBUG
		printf("CDVD: RPC Initialize\n");
	#endif

	SifInitRpc(0);

	// 0x4800 bytes for TocEntry structures (can fit 128 of them)
	// 0x400 bytes for the filename string
	buffer = AllocSysMemory(0, 0x4C00, NULL);
	if(buffer == NULL)
	{
		#ifdef DEBUG
  			printf("Failed to allocate memory for RPC buffer!\n");
		#endif

		SleepThread();
	}

	SifSetRpcQueue(&qd, GetThreadId());
	SifRegisterRpc(&sd0, CDVD_IRX,CDVD_rpc_server,(void *)buffer,0,0,&qd);
	SifRpcLoop(&qd);
}

void* CDVD_rpc_server(int fno, void *data, int size)
{

	switch(fno) {
		case CDVD_FINDFILE:
			return CDVDRpc_FindFile((unsigned*)data);
		case CDVD_GETDIR:
			return CDVDRpc_Getdir((unsigned*)data);
		case CDVD_STOP:
			return CDVDRpc_Stop();
		case CDVD_TRAYREQ:
			return CDVDRpc_TrayReq((unsigned*)data);
		case CDVD_DISKREADY:
			return CDVDRpc_DiskReady((unsigned*)data);
		case CDVD_FLUSHCACHE:
			return CDVDRpc_FlushCache();
	}

	return NULL;
}

void _splitpath (const char *constpath, char *dir, char *fname)
{
	// 255 char max path-length is an ISO9660 restriction
	// we must change this for Joliet or relaxed iso restriction support
	static char pathcopy[1024+1];

	char* slash;

	strncpy(pathcopy, constpath, 1024);

    slash = strrchr (pathcopy, '/');

	// if the path doesn't contain a '/' then look for a '\'
    if (!slash)
		slash = strrchr (pathcopy,  (int)'\\');

	// if a slash was found
	if (slash != NULL)
	{
		// null terminate the path
		slash[0] = 0;
		// and copy the path into 'dir'
		strncpy(dir, pathcopy, 1024);
		dir[255]=0;

		// copy the filename into 'fname'
		strncpy(fname, slash+1, 128);
		fname[128]=0;
	}
	else
	{
		dir[0] = 0;

		strncpy(fname, pathcopy, 128);
		fname[128]=0;
	}

}

// Copy a TOC Entry from the CD native format to our tidier format
void TocEntryCopy(struct TocEntry* tocEntry, struct dirTocEntry* internalTocEntry)
{
	int i;
	int filenamelen;

	tocEntry->fileSize = internalTocEntry->fileSize;
	tocEntry->fileLBA = internalTocEntry->fileLBA;
	tocEntry->fileProperties = internalTocEntry->fileProperties;

	if (CDVolDesc.filesystemType == 2)
	{
		// This is a Joliet Filesystem, so use Unicode to ISO string copy
		filenamelen = internalTocEntry->filenameLength/2;

		for (i=0; i < filenamelen; i++)
			tocEntry->filename[i] = internalTocEntry->filename[(i<<1)+1];
	}
	else
	{
		filenamelen = internalTocEntry->filenameLength;

		// use normal string copy
		strncpy(tocEntry->filename,internalTocEntry->filename,128);
	}

	tocEntry->filename[filenamelen] = 0;

	if (!(tocEntry->fileProperties & 0x02))
	{
		// strip the ;1 from the filename (if it's there)
		strtok(tocEntry->filename, ";");
	}


}

// Check if a TOC Entry matches our extension list
int TocEntryCompare(char* filename, const char* extensions)
{
	static char ext_list[129];

	char* token;

	char* ext_point;

	strncpy(ext_list,extensions,128);
	ext_list[128]=0;

	token = strtok( ext_list, " ," );
	while( token != NULL )
	{
		// if 'token' matches extension of 'filename'
		// then return a match
		ext_point = strrchr(filename,'.');

		if (strcasecmp(ext_point, token) == 0)
			return (TRUE);

		/* Get next token: */
		token = strtok( NULL, " ," );
	}

	// If not match found then return FALSE
	return (FALSE);

}

// Used in findfile
//int tolower(int c);
int strcasecmp(const char *s1, const char *s2)
{
	while (*s1 != '\0' && tolower(*s1) == tolower(*s2))
	{
		s1++;
		s2++;
	}

	return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2);
}

int strncasecmp(const char *s1, const char *s2, int limit)
{
	int i;

	for (i=0;i<limit;i++)
	{
		if (*s1 == '\0')
			return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2);

		if (tolower(*s1) != tolower(*s2))
			return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2);

		s1++;
		s2++;
	}

	return 0;
}

enum PathMatch ComparePath(const char* path)
{
	int length;
	int i;

	length=strlen(CachedDirInfo.pathname);

	for (i=0;i<length;i++)
	{
		// check if character matches
		if (path[i] != CachedDirInfo.pathname[i])
		{
			// if not, then is it just because of different path seperator ?
			if ((path[i] == '/') || (path[i] == '\\'))
			{
				if ((CachedDirInfo.pathname[i] == '/') || (CachedDirInfo.pathname[i]=='\\'))
				{
					continue;
				}
			}

			// if the characters don't match for any other reason then report a failure
			return NOT_MATCH;
		}
	}

	// Reached the end of the Cached pathname

	// if requested path is same length, then report exact match
	if (path[length] == 0)
		return MATCH;

	// if requested path is longer, and next char is a dir seperator
	// then report sub-dir match
	if ((path[length]=='/') || (path[length]=='\\'))
		return SUBDIR;
	else
		return NOT_MATCH;
}









