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

	files.c

	hard disc/cd-rom file handling

	(c) 1998 Interactive Studios Ltd.

*****************************************************************************/
                  
#include "glover.h"
#include <strings.h>	// to get rid of a warning at strrchr


//#define INLINE_DECOMPRESS	1

/**************************************
*          GLOBAL VARIABLES           *
**************************************/

UBYTE			*fileroot={"C:\\PSX\\WHACK\\CD\\"}; 
ULONG			lastfilelength=0;
UBYTE			filename[100];
UBYTE			*returnfile=0;
LONG			filehandle=0;
UBYTE			*mallocarea=0;
UBYTE			*filepoint=0;
ULONG			fileoffset;
CdlFILE			fp;
CdlFILE			fpcpy;						/* LIBCD FILE STRUCT, INCLUDING THE LENGTH */
UBYTE			cdstopped=NO;
UBYTE			cdresult[8];
UBYTE			numberofreaderrors=0;
LONG			firstsector=0;
ULONG			sectorstoread=0;
UBYTE			indexbuffer[8*2048];		//allows for 512 files
volatile LONG	startsector=0;
LONG			fileindex=0;
LONG			startDATsector=0;
LONG			checksuming=YES;
LONG			warningcnt=0;
UBYTE			filestatus=0;
ULONG			checksumoffiletable=0;
UBYTE			majorcderror=0;
UBYTE			*restartbuf;
UBYTE			CD_param[4];
LONG			Pcinitstate=0;
FILESECTOR		filesector[MAXSECTORFILES];
UBYTE			CDdatabuffer[2048];


/**************************************
*           FILE FUNCTIONS            *
**************************************/

void InitialiseFiles()
{

	UBYTE ret;

#if GOLDCD == NO
	if(goldcd == NO)
		Pcinitstate = PCinit();
#endif

   	if((goldcd == YES) || (dovideo == YES))
	{
 		//CDctrl.trackrequested = 0; CPW
 		CdInit();
	   	ret = CDload(indexbuffer,"\\GLOVER.DAT;1",8,0); //512

   	}
}

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

#if GOLDCD == NO

void CheckValidFilename(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;
	}
}

LONG FindFileLength(UBYTE *name)
{
	LONG length;

	CheckValidFilename(name);

 	filehandle = PCopen(name,0,0);	/* Open file */
	if (filehandle == -1)
	{
		debugPrintf("Error HD Couldn't find file %s\n",name);
		return -1;
	}

	length = PClseek(filehandle,0,2);	/* Get length of file */
	PCclose(filehandle);
	debugPrintf("%s: %d\n",name,length);
	return length;
}

#endif

/**********************************************************************************
* LOAD FILE BY DEFAULT FROM HARD DRIVE - BUT INTERCEPTED TO LOAD FROM CD DAT FILE *
**********************************************************************************/

UBYTE* LoadFileRaw(UBYTE* name)
{

	UBYTE	tempmess[34];
	UBYTE	*cp;
	
	//CDctrl.trackrequested=0; CPW
	filepoint=0;


//	if(cdmusic==YES && CDctrl.nStatus != CdlStop)	// Fred - I thought about calling crash here... (!)
	if(CDctrl.trackrequested!=0)
	{
//		PRINTF(" *** CD STILL DOING STUFF (status %d) AT A FILE-LOAD! ***\n",CDctrl.nStatus);
// (do the rest of the cd commands (file-loading, etc) stop the music anyway - Ie, was the problem
// just the IRQ re-seeking the track start?

// necessary to prevent the IRQ from trying to loop the audio track while the filer's loading stuff
		CDctrl.trackrequested = 0;
	}

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

  	if(goldcd == YES)
		return LoadCDFile(name);

	sprintf(filename,"%s%s",fileroot,name); 

#if GOLDCD == NO

	CheckValidFilename(filename);
	filehandle = PCopen(filename,0,0);	/* Open file */
	
	if (filehandle == -1) 
	{		  
		debugPrintf("Error HD Couldn't find file %s\n",filename);
		return (UBYTE*)-1;		
	}

	lastfilelength = PClseek(filehandle,0,2);	/* Get length of file */

	PClseek(filehandle,0,0);		/* reset file pointer */
#ifdef INLINE_DECOMPRESS
	PCread(filehandle,CDdatabuffer,8);
	PClseek(filehandle,0,0);		/* reset file pointer */
	lPtr = CDdatabuffer;
#endif

  	cp = strrchr(name,'\\');
	
	if(cp == NULL)
		cp = name;

  	sprintf(tempmess,"FILE-%s",cp); //strrchr(name,'\\'));
	
#ifdef INLINE_DECOMPRESS
	if (lPtr[0] == FLA_MAGIC)
	{
		filepoint = MALLOC(lPtr[1]+128,tempmess);
		cp = filepoint+(lPtr[1]-lastfilelength)+128;
		PCread(filehandle,cp,lastfilelength); /* Read file */
		DecompressBuffer(cp+8, filepoint);
		debugPrintf("################## DECOMPRESSED! ########################\n");
	}
	else
#endif
	{
		filepoint = MALLOC(lastfilelength,tempmess);	/* Allocate memory buffer for file */
		PCread(filehandle,filepoint,lastfilelength); /* Read file */
	}
	PCclose(filehandle);		/* Close file handle */
	
#endif //GOLDCD
	return filepoint;
}


UBYTE *LoadFile(UBYTE *name)
{
	UBYTE	*data, *realData;
	ULONG	*lPtr;

	data = LoadFileRaw(name);
	if ((int)data==-1)
		return NULL;
	if (data==NULL)
		return NULL;

/*	lPtr = (ULONG *)data;
	if (lPtr[0] == FLA_MAGIC)
	{
		realData = MALLOC(lPtr[1], name);
		if (realData!=NULL)
		{
			DecompressBuffer(data+8, realData);
			FREE(data);
			debugPrintf("READ FILE: %s [compressed]\n", name);
			return realData;
		}
	}
*/
	debugPrintf("READ FILE: %s\n", name);
	return data;
}

/***********************************************************
*       LOADFILE - FROM CD WITHIN THE LARGE DAT FILE       *
***********************************************************/
/*************************************************************************************/

UBYTE  CheckSumFile(UBYTE *filedata)
{
	ULONG k;
	ULONG sum;

	if(checksuming==ON)
	{
   		sum = 0;			
    	for(k = 0;k < lastfilelength;k++)
    	{
            sum += filedata[k];
    	}
   		
		if(sum != filechecksum[fileindex])
		{
	    	FPRINTF("checksum didnt match %lx,%lx index %d\n",sum,filechecksum[fileindex],fileindex);
        	return NO;
   		}
   }
   return YES;
}



UBYTE* LoadCDFile(UBYTE* name)		/*FROM within ACTION.DAT*/
{
	UBYTE	status;
	LONG	filelength = 0;

	debugPrintf("FROM CD\n");
	numberofreaderrors = 0;

reload:

	returnfile = 0;
	mallocarea = 0;
 	if (cdstopped == YES)
	{
		CdInit();
		cdstopped = NO;
	}

 	filestatus = FileInfo(indexbuffer,name,&firstsector,&lastfilelength,&fileindex);
	filelength = lastfilelength;
  	
	if(filestatus == 0)
	{
  		debugPrintf("---------Problem! %s file not in .DAT file\n",name);
		return NULL;	// Fred
  	} 
retry:

	if(numberofreaderrors >= 5)
		RestartCD();

	CdIntToPos(firstsector,&fp.pos);

	CdControl(CdlSetloc,(UBYTE*)&fp.pos,cdresult);

	if(cdresult[0]==0)
	{
		FPRINTF("Seek Error\n");
		goto error;
	}

#ifdef INLINE_DECOMPRESS
	if(!CdRead(1,(ULONG*)CDdatabuffer,CdlModeSpeed))
		goto error;

	status = ShowCDStatus();
	if(status == NO)
		goto error;

	lPtr = CDdatabuffer;
	CdControl(CdlSetloc,(UBYTE*)&fp.pos,cdresult);
	if(cdresult[0]==0)
	{
		FPRINTF("Seek Error\n");
		goto error;
	}

	if (lPtr[0] == FLA_MAGIC)
	{
		debugPrintf("### should be FLA\n");
		len = (((lPtr[1]>>11)+1)<<11)+1024;
	   	returnfile = (UBYTE*)MALLOC(len,"CD LOAD FILE AREA");
		cp = returnfile+( len - (((lastfilelength >> 11) + 1)<<11) );
		if(!CdRead((lastfilelength >> 11) + 1,(ULONG*)cp,CdlModeSpeed))
			goto error;
		status = ShowCDStatus();
		if(status == NO)
			goto error;
		lastfilelength = filelength;
		if(CheckSumFile(cp)==NO)
		{
			FREENULL(returnfile);
			numberofreaderrors++;
			debugPrintf("checksum failed...\n");
			goto reload;
		}
		DecompressBuffer(cp+8, returnfile);
		debugPrintf("################## DECOMPRESSED! ########################\n");
	}
	else
#endif
	{
	   	returnfile = (UBYTE*)MALLOC(((lastfilelength>>11)+1)<<11,"CD LOAD FILE AREA");
		if(!CdRead((lastfilelength >> 11) + 1,(ULONG*)returnfile,CdlModeSpeed))
			goto error;
		status = ShowCDStatus();
		if(status == NO)
			goto error;
		lastfilelength = filelength;
		if(CheckSumFile(returnfile)==NO)
		{
			FREENULL(returnfile);
			numberofreaderrors++;
			debugPrintf("checksum failed...\n");
			goto reload;
		}
	}

	majorcderror = 0;
   	warningcnt = 0;

   	return returnfile;	//normal exit

error:
	if(mallocarea != 0)
		FREENULL(mallocarea);
	if (returnfile != 0)
		FREENULL(returnfile);
	numberofreaderrors++;
	goto retry;
}

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

UBYTE ShowCDStatus()
{
	LONG CDstatus;
	LONG oldCDstatus;

	oldCDstatus = CDstatus = -2;
	
	while (CDstatus != 0)
	{
		CDstatus = CdReadSync(1,0);
		if(CDstatus == -1)
		{
			return NO;
		}
		if(oldCDstatus!=CDstatus)
		{
			oldCDstatus=CDstatus;
		}
	}
	return YES;
}


/****************************************/
/*	find file pointers from ACTION.DAT	*/
/****************************************/


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


	noof = *(LONG*)index;
	index += 4;

	cp = (BYTE*)strrchr((BYTE*)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)
		{
			FPRINTF("couldnt find file %s in DAT file",filespec);
			return 0;
		}

		if(ret<0)
			min = (min != pos) ? pos : pos + 1;
		else
			max = pos;
	}

	*firstsec = *(LONG*)(ep + 16) + startDATsector;
	*size = *(LONG*)(ep + 20);
	if((LONG)number!=0)
		*number = pos;
	return 1;
}

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

/**********************
*  LOAD FILE FROM CD *
*********************/

UBYTE CDload(BYTE *bufferarea,BYTE *filename,LONG no_of_sectors,LONG offset)
{	//if no_of_sectors= 0 it loads what size the file is
	//WARNING the file length is rounded up, therefore the malloc area must also be

	numberofreaderrors=0;
retry:
	
	if (numberofreaderrors>=5)
	{
	  //			RestartCD();
	}

	if(!CdSearchFile(&fp,filename))
	{
			numberofreaderrors++;
			FPRINTF("%s Not Found!\n",filename);
			goto retry;
	}

    lastfilelength = fp.size;

	debugPrintf("BS: Set loc for seek.%d,%d,%d,%d\n",
             fp.pos.track,fp.pos.minute,fp.pos.second,fp.pos.sector);
  

	startDATsector = CdPosToInt(&fp.pos);
 	startDATsector += offset;
	CdIntToPos(startDATsector,&fp.pos);

	
	if(no_of_sectors == 0)
	{
		no_of_sectors = lastfilelength / 2048;
		if(no_of_sectors * 2048 != lastfilelength)
			no_of_sectors++;
	}
			
	CdControl(CdlSetloc,(UBYTE*)&fp.pos,cdresult);

	if(cdresult[0] == 0)
	{
		numberofreaderrors++;
		FPRINTF("Seek Error\n");
		goto retry;
	}

	FPRINTF("CD file=%s - ",filename);

    if(!CdRead(no_of_sectors,(ULONG*)bufferarea,CdlModeSpeed))
    {
      	numberofreaderrors++;
      	FPRINTF("Error!\n");
      	goto retry;
     }

	if(ShowCDStatus()==NO)
	{
		numberofreaderrors++;
		goto retry;
	}
    
	return lastfilelength;			/* RETURN THE LENGTH OF THE FILE */
}

/**********************************************************************************/
void RestartCD()
{


	FPRINTF("CD restart!\n");
	numberofreaderrors = 0;

	majorcderror++;

	CdInit();
	cdstopped = NO;

	if(majorcderror > 2)
	{
		CdSearchFile(&fpcpy,"\\ACTION.DAT;1");
		CdControl(CdlSetloc,(UBYTE*)&fpcpy.pos, cdresult);
		restartbuf = 0;
		restartbuf = (UBYTE*)MALLOC(2048,"CDRESTSART");

    	CdRead(1,(ULONG*)restartbuf,CdlModeSpeed);
		CdSync(0,0);
		FREENULL(restartbuf);
		VSync(0);
		DrawSync(0);
		
		Set_Up_Display();
		FPRINTF("MAJOR!\n");
		majorcderror = 0;
	}
			   
}

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

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

LONG sector4file(UBYTE *filename)
{
	UBYTE j;

	for(j=0;j<MAXSECTORFILES;j++)
	{
		if(strcmp(filename,filesector[j].name) == 0)
		{
			return filesector[j].sector;
		}
	}

	for(j=0;j<MAXSECTORFILES;j++)
	{
		if(filesector[j].sector==0)
			break;
	}
	
	if(!CdSearchFile(&fp,filename))
	{
		debugPrintf("%s Not Found!\n",filename);
	}
	
	strcpy(filesector[j].name,filename);
	filesector[j].sector = CdPosToInt(&fp.pos);
	return 0;
}

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

#define cdSetVol(vol, v) (vol)->val0=(vol)->val2=v,(vol)->val1=(vol)->val3=0,CdMix(vol)

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

void SetCD2DataMode()
{
  	CdControl(CdlPause, 0, 0);
  	CdFlush();
 	CD_param[0] = 0;
 	CdControl(CdlSetmode,CD_param, 0);	
  	CdReset(0);
}

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