/************************************************************************************
	ACTION MAN PS	(c) 1998-9 ISL

	fileio.c:		New file handling

************************************************************************************/

#include "glover.h"
//#include <string.h>
//#include <string.h>
//#include "decomp.h"

#define FILEIO_PCROOT	"C:\\PSX\\WHACK\\CD\\"
#define FILEIO_CDINDEX	"\\GLOVER.DAT;1"


void fileCheckValidFilename(UBYTE *name);

typedef struct _FileIODataType {
	int		DATsector;
	UBYTE	*index;
	UBYTE	CDresult[8];
	char	bkgFilename[256];
	int		bkgLoading;
	UBYTE	*bkgBuffer;
} FileIODataType;


FileIODataType	fileIO;

// needed by loadDATbinary (ie, the main load routine when goldcd's enabled.
static char		sectorBuf[2048];

/* PC READING CODE *********************************************************************************/

#if GOLDCD==0

/**************************************************************************
	FUNCTION:	filePCInitialise()
	PURPOSE:	Initialise PC file I/O
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

static void filePCInitialise()
{
	if (PCinit())
		debugPrintf("Error initialising PC filing\n");
}


/**************************************************************************
	FUNCTION:	filePCLoad()
	PURPOSE:	Load in file from PC
	PARAMETERS:	Filename
	RETURNS:	Ptr to file data or NULL
**************************************************************************/

static UBYTE *filePCLoad(char *fName, int *length, int temp)
{
	LONG	fHandle, size;
	UBYTE	*buffer;
	char	pathName[256];

	strcpy(pathName, FILEIO_PCROOT);
	strcat(pathName, fName);
	if ((fHandle = PCopen(pathName, 0,0))==-1)
	{
		debugPrintf("Error opening PC file '%s'\n", pathName);
		return NULL;
	}
	size = PClseek(fHandle, 0,2);
	PClseek(fHandle, 0,0);

	if(temp)
	{
		if ((buffer = TEMPMALLOC(size, fName))==NULL)
		{
			debugPrintf("Error allocating PC file '%s'\n", fName);
			PCclose(fHandle);
			return NULL;
		}
	}
	else
	{
		if ((buffer = MALLOC(size, fName))==NULL)
		{
			debugPrintf("Error allocating PC file '%s'\n", fName);
			PCclose(fHandle);
			return NULL;
		}

	}

	if (PCread(fHandle, buffer, size)!=size)
	{
		debugPrintf("Error reading PC file '%s'\n", fName);
		FREE(buffer);
		PCclose(fHandle);
		return NULL;
	}
	PCclose(fHandle);
	*length = size;
	return buffer;
}


/**************************************************************************
	FUNCTION:	filePCLoadBinary()
	PURPOSE:	Load in file to specific location (for overlays etc.)
	PARAMETERS:	Filename and location
	RETURNS:	0 if success, 1 if fail
**************************************************************************/

static UBYTE filePCLoadBinary(char *fName, char *loc)
{
	LONG	fHandle, size;
	char	pathName[256];

	debugPrintf("\nLoading binary code file %s\n",fName);

	strcpy(pathName, FILEIO_PCROOT);
	strcat(pathName, fName);
	if ((fHandle = PCopen(pathName, 0,0))==-1)
	{
		debugPrintf("Error opening PC file '%s'\n", pathName);
		return 1;
	}
	size = PClseek(fHandle, 0,2);
	PClseek(fHandle, 0,0);
	
	if (PCread(fHandle, loc, size)!=size)
	{
		debugPrintf("Error reading PC file '%s'\n", fName);
		PCclose(fHandle);
		return 1;
	}
	PCclose(fHandle);

	return 0;
}


#endif



/* CD READING CODE *********************************************************************************/

#if GOLDCD==1


/**************************************************************************
	FUNCTION:	fileCDreadIndex()
	PURPOSE:	Read index sectors from CD
	PARAMETERS:	Filename of DAT file
	RETURNS:	>0 if errors
**************************************************************************/

static int fileCDreadIndex(char *fName)
{
	CdlFILE	fp;

	if (CdSearchFile(&fp, fName)==NULL)									// Find file location
	{
		debugPrintf("Reading CD index: CdSearchFile() error\n");
		return 1;
	}
	fileIO.DATsector = CdPosToInt(&fp.pos);
	if (CdReadFile(fName, (ULONG *)fileIO.index, 8*2048)==0)			// Read file
	{
		debugPrintf("Reading CD index: CdReadFile() error\n");
		return 2;
	}
	if (CdReadSync(0, fileIO.CDresult))										// Wait for read
	{
		debugPrintf("Reading CD index: Read error\n");
		return 3;
	}
	return 0;
}


/**************************************************************************
	FUNCTION:	fileCDInitialise()
	PURPOSE:	Initialise CD file I/O
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

static void fileCDInitialise()
{
	CdInit();
	fileIO.index = MALLOC(8*2048, "CD index");
	while(fileCDreadIndex(FILEIO_CDINDEX))
		debugPrintf("Problem reading CD index - retry\n");
	debugPrintf("Read CD index\n");
}


/**************************************************************************
	FUNCTION:	fileCDGetLocation()
	PURPOSE:	Get location info from CD index
	PARAMETERS:	Index, filename, *first sector, *file length, *file number
	RETURNS:	>0 if errors
**************************************************************************/

static LONG fileCDGetLocation(UBYTE *index, char *filespec, LONG *firstsec, LONG *size, LONG *number)
{
	LONG	noof, min,max, pos, ret;
	BYTE	*cp, *ep;

	noof = *(LONG*)index;
	index += 4;
	cp = (BYTE*)strrchr((char *)filespec, '\\');
	if(cp == NULL)
		cp = filespec;
	else
		cp++;
	min = 0;
	max = noof-1;
	for(;;)
	{
		pos = (min + max) >> 1;
		ep = index + (pos << 5);
		ret = strcmp(ep, cp);
		if(ret == 0)
			break;
		if(min == max)
		{
			debugPrintf("Error file %s not in DAT\n", filespec);
			*size = 0;	// Fred - returning a special value for not present.
			return 0;
		}
		if(ret<0)
			min = (min != pos) ? pos : pos + 1;
		else
			max = pos;
	}
	*firstsec = *(LONG*)(ep + 16) + fileIO.DATsector;
	*size = *(LONG*)(ep + 20);
	if((LONG)number!=0)
		*number = pos;
	return 0;
}


/**************************************************************************
	FUNCTION:	fileCDloadDATfile()
	PURPOSE:	Load in file from CD DAT file
	PARAMETERS:	Filename, buffer
	RETURNS:	Ptr to data or NULL
**************************************************************************/

static ULONG *fileCDloadDATfile(char *fName, int *length, int temp)
{
	LONG	sec, sz, num;
	CdlFILE	fp;
	ULONG	*buffer;

//	debugPrintf("file load attempt %s\n",fName);

	if (fileCDGetLocation(fileIO.index, fName, &sec, &sz, &num))
		return NULL;

	if(!sz)		// Fred, dealing with non-existant files
		return ((ULONG *)(1));

	if(temp)
	{
		if ((buffer = TEMPMALLOC(((sz>>11)+1)<<11, fName))==NULL)				// Get RAM
		{
			debugPrintf("Load file: Not enough RAM\n");
			return NULL;
		}
	}
	else
	{
		if ((buffer = MALLOC(((sz>>11)+1)<<11, fName))==NULL)				// Get RAM
		{
			debugPrintf("Load file: Not enough RAM\n");
			return NULL;
		}
	}

	CdIntToPos(sec, &fp.pos);
	if (CdControl(CdlSetloc, (UBYTE *)&fp.pos, fileIO.CDresult)==0)		// Seek to file
	{
		debugPrintf("Load file: CDControl() error\n");
		FREE(buffer);
		return NULL;
	}
	if (fileIO.CDresult[0]==0)											// Check seek OK
	{
		debugPrintf("Load file: Seek error\n");
		FREE(buffer);
		return NULL;
	}
	if (CdRead((sz>>11)+1, buffer, CdlModeSpeed)==0)					// Start read
	{
		debugPrintf("Load file: CdRead() error\n");
		FREE(buffer);
		return NULL;
	}
	*length = sz;
	return buffer;
}


/**************************************************************************
	FUNCTION:	fileCDcheckLoaded()
	PURPOSE:	Load in file from CD
	PARAMETERS:	
	RETURNS:	0 = Ready	<0 = Errors		>0 = Not ready yet
**************************************************************************/

static int fileCDcheckLoaded()
{
	return CdReadSync(1, fileIO.CDresult);
}


/**************************************************************************
	FUNCTION:	fileCDLoad()
	PURPOSE:	Load in file from CD
	PARAMETERS:	Filename
	RETURNS:	Ptr to file data or NULL
**************************************************************************/

static UBYTE *fileCDLoad(char *fName, int *length, int temp)
{
	ULONG	*buffer;
	int		rtn;
	int		store;
	store = interrupt_cyclebar;
	interrupt_cyclebar = 1;

	while (1)
	{
		if ((buffer = fileCDloadDATfile(fName, length,temp))!=NULL)
		{
			if((ULONG)buffer==1)	// Fred, dealing with non-existant files
			{
				interrupt_cyclebar = store;
				return NULL;
			}
			while((rtn=fileCDcheckLoaded())>0);
			if (rtn==0)
			{
				interrupt_cyclebar = store;

				return (UBYTE *)buffer;
			}
		}		
		debugPrintf("Problem reading file - retry\n");
	}
	interrupt_cyclebar = store;
	return (UBYTE *)buffer;
}


/**************************************************************************
	FUNCTION:	fileCDLoadBkg()
	PURPOSE:	Start background loading file from CD
	PARAMETERS:	Filename
	RETURNS:	
**************************************************************************/

static UBYTE *fileCDLoadBkg(char *fName, int *length)
{
	fileIO.bkgLoading = 1;
	while ((fileIO.bkgBuffer=(UBYTE *)fileCDloadDATfile(fName, length,0))==NULL)
	{
		if ((ULONG)(fileIO.bkgBuffer) == 1)
			return NULL;
		debugPrintf("Problem reading file - retry\n");
	}
	return(UBYTE *)fileIO.bkgBuffer;
}


/**************************************************************************
	FUNCTION:	fileCDCheckBkg()
	PURPOSE:	Check on CD loading status, retry if needed
	PARAMETERS:	
	RETURNS:	fileIO.bkgLoading = 0 if finished
**************************************************************************/

static void fileCDCheckBkg()
{
	int		rtn, len;

	rtn = fileCDcheckLoaded();
	if (rtn==0)
	{
		DB("dun\n");
		fileIO.bkgLoading = 0;
	}
	if (rtn<0)
	{
		DB("calling direct\n");
		fileCDloadDATfile(fileIO.bkgFilename, &len,0);
	}
	if(rtn > 0)
		DB(".");
}


/**************************************************************************
	FUNCTION:	fileCDloadDATbinary()
	PURPOSE:	Load in file from CD DAT file
	PARAMETERS:	Filename, buffer
	RETURNS:	Ptr to data or NULL
**************************************************************************/

static UBYTE fileCDloadDATbinary(char *fName, int *loc)
{
	LONG	sec, sz, num;
	CdlFILE	fp;
	int		rtn;
	UBYTE	*ptr;

	if (fileCDGetLocation(fileIO.index, fName, &sec, &sz, &num))
		return 1;
	CdIntToPos(sec, &fp.pos);
	if (CdControl(CdlSetloc, (UBYTE *)&fp.pos, fileIO.CDresult)==0)		// Seek to file
	{
		debugPrintf("Load file: CDControl() error\n");
		return 1;
	}
	if (fileIO.CDresult[0]==0)											// Check seek OK
	{
		debugPrintf("Load file: Seek error\n");
		return 1;
	}
	if (CdRead((sz>>11), (void *)loc, CdlModeSpeed)==0)							// Start read
	{
		debugPrintf("Load file: CdRead() error\n");
		return 1;
	}

	while((rtn=fileCDcheckLoaded())>0);									// Wait for read

	CdIntToPos(sec+(sz>>11), &fp.pos);
	if (CdControl(CdlSetloc, (UBYTE *)&fp.pos, fileIO.CDresult)==0)		// Seek to file
	{
		debugPrintf("Load file: CDControl() error\n");
		return 1;
	}
	if (fileIO.CDresult[0]==0)											// Check seek OK
	{
		debugPrintf("Load file: Seek error\n");
		return 1;
	}
	if (CdRead(1, (void *)sectorBuf, CdlModeSpeed)==0)							// Start read last bit
	{
		debugPrintf("Load file: CdRead() error\n");
		return 1;
	}

	while((rtn=fileCDcheckLoaded())>0);									// Wait for read

	ptr = (UBYTE *)loc;
	ptr += ((sz>>11)<<11);
	memcpy(ptr, sectorBuf, sz & 0x7ff);
	return 0;
}

#endif



/* EXTERNAL INTERFACE ******************************************************************************/


/**************************************************************************
	FUNCTION:	fileInitialise()
	PURPOSE:	Initialise file I/O
	PARAMETERS:	
	RETURNS:	
**************************************************************************/

void fileInitialise()
{
#if GOLDCD==1
	fileCDInitialise();
#else
	filePCInitialise();
#endif
}


/**************************************************************************
	FUNCTION:	fileLoad()
	PURPOSE:	Load in file
	PARAMETERS:	Filename, ptr to length var
	RETURNS:	Ptr to file data or NULL
**************************************************************************/

UBYTE *fileLoad(UBYTE *fName, int *length)
{
	UBYTE	*data;
	ULONG	*lPtr;
	int		len;

	if(XAplaying)
	{
		PRINTF("forcing XA stop\n");
		XAstop();
	}

#if GOLDCD==1
	data = fileCDLoad(fName, &len,0);
#else
	fileCheckValidFilename(fName);
	data = filePCLoad(fName, &len,0);
#endif
	if (data==NULL)
		return NULL;

	lPtr = (ULONG *)data;
/*	if (lPtr[0] == FLA_MAGIC) // we're not using compressed stuff (CPW)
	{
		realData = MALLOC(lPtr[1], fName);
		if (realData!=NULL)
		{
			DecompressBuffer(data+8, realData);
			FREE(data);
			debugPrintf("READ FILE: %s [compressed]\n", fName);
			return realData;
		}
	}
*/
	debugPrintf("READ FILE: %s\n", fName);
	if (length!=NULL)
		*length = len;
	return data;
}

UBYTE *fileTempLoad(UBYTE *fName, int *length)
{
	UBYTE	*data, *realData;
	ULONG	*lPtr;
	int		len;

	if(XAplaying)
	{
//		PRINTF(" *** XA STILL DOING STUFF AT A FILE-LOAD! ***\n");
		PRINTF("forcing XA stop\n");
		XAstop();
	}

#if GOLDCD==1
	data = fileCDLoad(fName, &len,1);
#else
	fileCheckValidFilename(fName);
	data = filePCLoad(fName, &len,1);
#endif
	if (data==NULL)
		return NULL;

	lPtr = (ULONG *)data;
/*	if (lPtr[0] == FLA_MAGIC) // we're not using compressed stuff (CPW)
	{
		realData = MALLOC(lPtr[1], fName);
		if (realData!=NULL)
		{
			DecompressBuffer(data+8, realData);
			FREE(data);
			debugPrintf("READ FILE: %s [compressed]\n", fName);
			return realData;
		}
	}
*/
	debugPrintf("READ FILE: %s\n", fName);
	if (length!=NULL)
		*length = len;
	return data;
}

/**************************************************************************
	FUNCTION:	fileLoad()
	PURPOSE:	Load in file
	PARAMETERS:	Filename
	RETURNS:	Ptr to file data or NULL
**************************************************************************/

//UBYTE *fileLoad(UBYTE *fName)
//{
//	return fileLoad(fName, NULL);
/*
	UBYTE *addr;
	int tick;
	if(!fileBkgLoad(fName, NULL,0))
		return NULL;
	while(! (addr = fileBkgComplete()))
	{
		tick++;
//		if(!(tick & 1))
			DB(".");
	}
*/
//}

//UBYTE *fileTempLoad(UBYTE *fName)
//{
//
//	return fileTempLoad2(fName, NULL);
/*
	UBYTE *addr;
	int tick;
	if(!fileBkgLoad(fName, NULL,1))
		return NULL;
	while(! (addr = fileBkgComplete()) )
	{
		tick++;
//		if(!(tick & 1))
			DB(".");
	}
*/
//}

/**************************************************************************
	FUNCTION:	fileBkgLoad()
	PURPOSE:	Load file in background
	PARAMETERS:	Filename
	RETURNS:	
**************************************************************************/

int fileBkgLoad(UBYTE *fName, int *length, int temp)
{
	int	len;

	if(XAplaying)
	{
//		PRINTF(" *** XA STILL DOING STUFF AT A FILE-LOAD! ***\n");
		PRINTF("forcing XA stop\n");
		XAstop();
	}

	strcpy(fileIO.bkgFilename, fName);
#if GOLDCD==1
	if(!fileCDLoadBkg(fName, &len))
		return 0;
#else
	fileIO.bkgBuffer = filePCLoad(fName, &len,temp);
	fileIO.bkgLoading = 0;
#endif
	debugPrintf("READ FILE: %s [bkg start]\n", fName);
	if (length!=NULL)
		*length = len;
	return 1;
}


/**************************************************************************
	FUNCTION:	fileBkgComplete()
	PURPOSE:	Check on file loading in background
	PARAMETERS:	
	RETURNS:	Ptr to buffer or NULL
**************************************************************************/

UBYTE *fileBkgComplete()
{
#if GOLDCD==1
	fileCDCheckBkg();
#endif
	if (fileIO.bkgLoading)
		return NULL;
	debugPrintf("READ FILE: %s [bkg complete]\n", fileIO.bkgFilename);
	return fileIO.bkgBuffer;
}


/**************************************************************************
	FUNCTION:	fileLoadBinary()
	PURPOSE:	Load in file to specific location (for overlays etc.)
	PARAMETERS:	Filename and location
	RETURNS:	0 if success, 1 if fail
**************************************************************************/

UBYTE fileLoadBinary(UBYTE *fName, UBYTE *loc)
{
#if GOLDCD==1
	return fileCDloadDATbinary(fName, (int *)loc);
#else
	return filePCLoadBinary(fName, loc);
#endif
}

#if GOLDCD == NO

void fileCheckValidFilename(UBYTE *name)
{
	UBYTE *work;
	LONG dot,slash,final,i;

	i = dot = slash = final = 0;
	dot = -1;
	slash = -1;
	final = -1;
	for(work = name;*work;work++,i++)
	{
		if( (*work) == '.')
			dot = i;
		if( (*work) == '\\')
			slash = i;
	}
	final = i;

	if(dot==-1)	// if no suffix, suffix is at the end
		dot = i;

//	if(slash == -1)	// if no prefix, prefix is at the beginning
//		slash = -1;

	if(dot-slash > 9)
	{
		DB("*** %s is not a valid playstation file name ***\n",name);
		CRASH;
	}
}

#endif