// ------------------------------------------------------------------------------------------------
// MyFile.c
//
// Contains IO procs for the streamer and file systems and a basic file library
//
// COPYRIGHT (C) SEGA OF AMERICA, INC. 1998
// All rights reserved.
//
// Information and source code samples contained herein are 
// provided "as-is", without representations or warranties, and 
// are subject to change without notice.
//
// SEGA OF AMERICA, INC. cannot support modifications or 
// derivative works created from the sample source code provided. 
// Developers may use, reproduce, and modify portions or entire 
// sections of the sample source code for the purposes of creating
// Dreamcast applications.  
//
// Sample source code contained herein may not be appropriate 
// for use under all circumstances.  Please consult Developer 
// documentation provided along with your Dreamcast unit for
// additional information and suggestions.
//
//
// Author: Ralph "Cooksey" Thomas
//
// Tabs:	4
//
// Rev:		04/05/99	Added this header
//			04/07/99	Cleaned up.
// ------------------------------------------------------------------------------------------------

#include <Shinobi.h>
#include <am.h>
#include <acassert.h>
#include "MyFile.h"
#include "MyPrintf.h"


#define MY_FILE_MAX_HANDLES		32

GDFS	fileHandleArray[MY_FILE_MAX_HANDLES];

KTBOOL	_MyFileAllocateHandle(ACFILE *fd)
{
KTU32	i	=	0;

for(i=0;i<MY_FILE_MAX_HANDLES;i++)
	{
	if(fileHandleArray[i]==KTNULL)
		{
		*fd	=	i;
		return KTTRUE;
		}
	}
	
return KTFALSE;
}


void _MyFileFreeHandle(ACFILE fd)
{
fileHandleArray[fd]	=	KTNULL;
}

void MyFileInit(void)
{
memset(&fileHandleArray,0x00,sizeof(fileHandleArray));
}

KTBOOL MyFileIoProc(KTSTRING fileName,ACFILE * fd,KTU8 * buffer,KTU32 * size,AM_FILE_OPERATION_MODE mode)
{
ACFILE		fileDescriptor	=	0;
KTU32		fileSize		=	0;
KTU8 *	fileBuffer		=	KTNULL;

if(mode==AM_OPEN)
	{
	// open the file
	if(MyFileOpen(fileName,fd)==KTFALSE)
		return KTFALSE;
	}
else if(mode==AM_CLOSE)
	{
	// close the file
	if(MyFileClose(*fd)==KTFALSE)
		return KTFALSE;
	}
else if(mode==AM_READ)
	{
	// read from the file
	if(MyFileRead(*fd,buffer,*size)==KTFALSE)
		return KTFALSE;
	}
else if(mode==AM_LOAD)
	{
	ACFILE gd;

	if(MyFileGetSize(fileName,&fileSize)==KTFALSE)
		return KTFALSE;

	if(MyFileOpen(fileName,&gd)==KTFALSE)
		return KTFALSE;

	// read from the file
	if(MyFileRead(gd,buffer,fileSize)==KTFALSE)
		return KTFALSE;
	
	if(MyFileClose(gd)==KTFALSE)
		return KTFALSE;
	}
else if(mode==AM_GET_SIZE)
	{
	// get the files size
	if(MyFileGetSize(fileName,size)==KTFALSE)
		return KTFALSE;
	}
else if(mode==AM_REWIND)
	{
	// get the files size
	if(MyFileRewind(*fd)==KTFALSE)
		return KTFALSE;
	}
else
	return KTFALSE;		// bad arg for mode, it should never come to this.

return KTTRUE;
}


KTBOOL MyStreamIoProc(KTSTRING fileName,ACFILE * sd,KTU8 * buffer,KTU32 * size,AM_FILE_OPERATION_MODE mode)
{
KTU32		fileDescriptor	=	0;
KTU32		fileSize		=	0;
KTU8 *	fileBuffer		=	KTNULL;

if(mode==AM_OPEN)
	{
	// open the file
	if(MyFileOpen(fileName,(ACFILE *)sd)==KTFALSE)
		return KTFALSE;
	}
else if(mode==AM_CLOSE)
	{
	// close the file
	if(MyFileClose((ACFILE)*sd)==KTFALSE)
		return KTFALSE;
	}
else if(mode==AM_READ)
	{
	// read from the file
	if(MyFileRead((ACFILE)*sd,buffer,*size)==KTFALSE)
		return KTFALSE;
	}
else if(mode==AM_GET_SIZE)
	{
	// get the files size
	if(MyFileGetSize(fileName,size)==KTFALSE)
		return KTFALSE;
	}
else if(mode==AM_REWIND)
	{
	// get the files size
	if(MyFileRewind(*sd)==KTFALSE)
		return KTFALSE;
	}
else
	return KTFALSE;		// bad arg for mode, it should never come to this.

return KTTRUE;
}


// STARTDOC ---------------------------------------------------------------------------------------
// FUNCTION NAME MyFileLoad
// TERSE DESCRIPTION Mid-level file load.
//
// FORMAT
//
// #include <am.h>
//
// KTBOOL MyFileLoad(KTSTRING fileName,volatile KTU32 *buffer)
// 
// PARAMETERS
//
// KTSTRING fileName,		The name of the file to be loaded
//
// volatile KTU32 *buffer,	A pointer to a buffer big enough to hold the file.
//
// RETURN VALUE
//
// KTBOOL, KTTRUE if the load operation was successful
// 
// FUNCTION
//
// Uses the low level MyFile calls to load a file, i.e. MyFileGetSize()->MyFileOpen()->
// MyFileRead()->MyFileClose()
// 
// If any of these calls fail it will send that calls error number to the error system.
//
// Note: All GD file system calls currently require that the buffer pointer passed in be aligned
// on a 32 byte boundry.
//
// ENDDOC -----------------------------------------------------------------------------------------


KTBOOL MyFileLoad(KTSTRING fileName,volatile KTU32 *buffer)
{
KTU32 		fileSize	=	0;
ACFILE		gd			=	KTNULL;

if(fileName==KTNULL || buffer==KTNULL)
	{
	acErrorSet(AC_NULL_PARAMETER,"MyFileLoad - fileName of buffer is NULL");
	return KTFALSE;
	}

if(MyFileGetSize(fileName,&fileSize)==KTFALSE)
	return KTFALSE;

if(MyFileOpen(fileName,&gd)==KTFALSE)
	return KTFALSE;

// read from the file
if(MyFileRead(gd,(KTU8 *)buffer,fileSize)==KTFALSE)
	return KTFALSE;

if(MyFileClose(gd)==KTFALSE)
	return KTFALSE;

return KTTRUE;
}
 
 
 
// STARTDOC ---------------------------------------------------------------------------------------
// FUNCTION NAME MyFileRewind
// TERSE DESCRIPTION Seeks to start of file.
//
// FORMAT
//
// #include <am.h>
//
// KTBOOL MyFileRewind(ACFILE fd)
// 
// PARAMETERS
//
// ACFILE fd, the file descriptor obtained by the call to MyFileOpen()
//
// RETURN VALUE
//
// KTBOOL, KTTRUE if the seek operation was successful
// 
// FUNCTION
//
// 
// Note: All GD file system calls currently require that the buffer pointer passed in be aligned
// on a 32 byte boundry.
//
// ENDDOC -----------------------------------------------------------------------------------------


KTBOOL MyFileRewind(ACFILE fd)
{
assert(fd != NULL,"MyFileRewind - fd is 0");

if(fileHandleArray[fd]==NULL)
	{
	acErrorSet(AC_NULL_PARAMETER,"MyFileRewind - fd is NULL");
	return KTFALSE;
	}

// ----- end parameter testing -----

if(gdFsSeek(fileHandleArray[fd],0,GDD_SEEK_SET) != GDD_ERR_OK)
	{
	acErrorSet(AC_SEEK_FAIL,"MyFileRewind - gdFsSeek fail");
	return KTFALSE;
	}

return KTTRUE;
}


// STARTDOC ---------------------------------------------------------------------------------------
// FUNCTION NAME MyFileRead
// TERSE DESCRIPTION Reads from a file using the GD file system.
//
// FORMAT
//
// #include <am.h>
//
// KTBOOL MyFileRead(ACFILE fd,KTU8 * buffer,KTU32 size) 
// 
// PARAMETERS
//
// ACFILE fd, the file descriptor obtained by the call to MyFileOpen()
//
// KTU8 * buffer, a pointer to a buffer of adequate size to accomodate the data
//
// KTU32 size, the size of the data to be read
// 
// RETURN VALUE
//
// KTBOOL, KTTRUE if the read operation was successful
// 
// FUNCTION
//
// Reads from a file using the GD file system
// 
// Note: All GD file system calls currently require that the buffer pointer passed in be aligned
// on a 32 byte boundry.
//
// ENDDOC -----------------------------------------------------------------------------------------

KTBOOL MyFileRead(ACFILE fd,KTU8 * buffer,KTU32 size)
{
KTU32	numberOfSectors	=	0;
KTU32	sectorSize		=	2048;
KTU32	moduloSize		=	0;
KTU32	remainder		=	0;
KTU8	tempBuffer[2048+32];
KTU8 *	tempPtr			=	&tempBuffer[0];
KTU32	tempOff			=	0;
KTU8 *	writeCursor		=	buffer;
Sint32	error			=	0;

assert(fd != KTNULL,"MyFileRead - fd is NULL");

if(fileHandleArray[fd]==NULL)
	{
	acErrorSet(AC_NULL_PARAMETER,"MyFileRead - fd is NULL");
	return KTFALSE;
	}

assert(buffer != KTNULL,"MyFileRead - buffer is NULL");

if(buffer==KTNULL)
	{
	acErrorSet(AC_NULL_PARAMETER,"MyFileRead - buffer is NULL");
	return KTFALSE;
	}

assert(!((KTU32)buffer % AM_FILE_BOUNDRY_ALIGNMENT),"MyFileRead - buffer is misaligned");	

if(((KTU32)buffer) % AC_FILE_BOUNDRY_ALIGNMENT)
	{
	acErrorSet(AC_NON_32_BYTE_ALIGNED_ADDRESS,"MyFileRead - buffer is not 32 byte aligned");
	return KTFALSE;
	}

assert(size != 0,"MyFileRead - size is 0");

if(size==0)
	{
	acErrorSet(AC_0_SIZE_PARAMETER,"MyFileRead - size is 0");
	return KTFALSE;
	}

// ----- end parameter testing -----

remainder	=	(size % sectorSize);

if(remainder) // works
	{
	moduloSize		=	(size-remainder) + sectorSize;
	numberOfSectors	=	moduloSize/sectorSize;
	}
else
	numberOfSectors	=	size/sectorSize;


if(remainder==0)	// it is an even number of sectors read once
	{
	if((error=gdFsRead(fileHandleArray[fd],numberOfSectors,buffer)) != GDD_ERR_OK)
		{
		acErrorSet(AC_READ_FAIL,"MyFileRead - gdFsRead failed");
		return KTFALSE;
		}
	
	}
else
	{
	memset(&tempBuffer[0],0x00,remainder); // dont set the whole buffer if not necessary

	if(gdFsRead(fileHandleArray[fd],numberOfSectors-1,buffer) != GDD_ERR_OK)
		{
		acErrorSet(AC_READ_FAIL,"MyFileRead - gdFsRead failed");
		return KTFALSE;
		}

	// fix up the tempPtr

	if(((unsigned long)tempPtr) % 32) // works
		{
		tempOff	= 32 - 	(((unsigned long)tempPtr) % 32);
		tempPtr += tempOff;
		}

	//acPrintf("Read PARTIAL sector fd=%p,numberOfSectors=%u,buffer=%p\n",fd,1,buffer);

	if(gdFsRead(fileHandleArray[fd],1,tempPtr) != GDD_ERR_OK)
		{
		acErrorSet(AC_READ_FAIL,"MyFileRead - gdFsRead failed");
		return KTFALSE;
		}

	writeCursor	=	buffer + ((numberOfSectors-1) * 2048);

	memcpy(writeCursor,tempPtr,remainder); // paste it at the end of the last read
	}

return KTTRUE;
}


// STARTDOC ---------------------------------------------------------------------------------------
// FUNCTION NAME MyFileOpen
// TERSE DESCRIPTION Opens a file using the GD file system
//
// FORMAT
//
// #include <am.h>
//
// KTBOOL MyFileOpen(KTSTRING fileName,ACFILE *fd)
// 
// PARAMETERS
//
// KTSTRING fileName, the name of the file to be opened
//
// ACFILE *fd, the file descriptor for the file is returned in this value
// 
// RETURN VALUE
//
// KTBOOL, KTTRUE if the file was successfully opened
// 
// FUNCTION
//
// Uses the GD file system to open a file for reading.
// 
// ENDDOC -----------------------------------------------------------------------------------------

KTBOOL MyFileOpen(KTSTRING fileName,ACFILE *fd)
{
GDFS	gd;
ACFILE	descriptor;
	
assert(fileName != KTNULL,"MyFileOpen - fileName is NULL");

if(fileName==KTNULL)
	{
	acErrorSet(AC_NULL_PARAMETER,"MyFileOpen - fileName is NULL");
	return KTFALSE;
	}

//assert(fd != KTNULL,"MyFileOpen - fd is NULL");	

if(fd==KTNULL)
	{
	acErrorSet(AC_NULL_PARAMETER,"MyFileOpen - fd is NULL");
	return KTFALSE;
	}

// ----- end parameter testing -----

if(_MyFileAllocateHandle(&descriptor)==KTFALSE)
	{
	MyDebugf("%s","MyFileOpen() Internal error, no more free file handles...");
	return KTFALSE;
	}

fileHandleArray[descriptor]=gdFsOpen(fileName,NULL);

if(fileHandleArray[descriptor]==NULL)
	{
	_MyFileFreeHandle(descriptor);
	acErrorSet(AC_FILE_NOT_FOUND,"MyFileOpen - file not found");
	return KTFALSE;
	}
else
	*fd	=	descriptor;
			
return KTTRUE;
}

// STARTDOC ---------------------------------------------------------------------------------------
// FUNCTION NAME MyFileClose
// TERSE DESCRIPTION Closes a file using the GD file system.
//
// FORMAT
//
// #include <am.h>
//
// KTBOOL MyFileClose(ACFILE fd)
// 
// PARAMETERS
//
// ACFILE fd, the file descriptor of the file to be closed
// 
// RETURN VALUE
//
// KTBOOL, KTTRUE if the file was successfully closed
// 
// FUNCTION
//
// Closes a file using the GD file system
// 
// ENDDOC -----------------------------------------------------------------------------------------


KTBOOL MyFileClose(ACFILE fd)
{
//assert(fd != NULL,"MyFileClose - fd is 0");

if(fileHandleArray[fd]==NULL)
	{
	acErrorSet(AC_NULL_PARAMETER,"MyFileClose - fd is 0");
	return KTFALSE;
	}

gdFsClose(fileHandleArray[fd]);

_MyFileFreeHandle(fd);

return KTTRUE;
}


// STARTDOC ---------------------------------------------------------------------------------------
// FUNCTION NAME MyFileGetSize
// TERSE DESCRIPTION Gets a files size using the GD file system.
//
// FORMAT
//
// #include <am.h>
//
// KTBOOL MyFileGetSize(KTSTRING fileName, KTU32 * size) 
// 
// PARAMETERS
//
// KTSTRING fileName, the name of the file for which the size is to be obtained
// KTU32 * size, the size of the named file is returned here if KTTRUE
// 
// RETURN VALUE
//
// KTBOOL, KTTRUE if the size was successfully gotten, size is 0 on fail.
// 
// FUNCTION
//
// Gets the size in bytes of the named file
// 
// ENDDOC -----------------------------------------------------------------------------------------


KTBOOL MyFileGetSize(KTSTRING fileName, KTU32 * size)
{
GDFS	fp			=	NULL;
Sint32	fileSize	=	0;


assert(size != NULL,"MyFileGetSize - size is NULL");

if(size==NULL)
	{
	acErrorSet(AC_NULL_PARAMETER,"MyFileGetSize - size is NULL");
	return KTFALSE;
	}

assert(fileName != KTNULL,"MyFileGetSize - fileName is NULL");

if(fileName==KTNULL)
	{
	acErrorSet(AC_NULL_PARAMETER,"MyFileGetSize - fileName is NULL");
	return KTFALSE;
	}

// ----- end parameter testing -----

if((fp=gdFsOpen(fileName,NULL))==NULL)
	{
	acErrorSet(AC_FILE_NOT_FOUND,"MyFileGetSize - gdFsOpen fail (file not found)");
	return KTFALSE;
	}

if(gdFsGetFileSize(fp,&fileSize) != TRUE)
	{
	acErrorSet(AC_GET_SIZE_FAIL,"MyFileGetSize - gdFsGetFileSize fail");
	gdFsClose(fp);
	return KTFALSE;
	}
	
gdFsClose(fp);

*size	=	fileSize;

return KTTRUE;   
}


