
#include <sys\types.h>
#include <libcd.h>
#include <libetc.h>
#include <stdio.h>
#include <libspu.h>
#include "typedefs.h"
#include "memory.h"
#include "gpu.h"
#include "cd.h"
#include "load_lev.h"
#include "specific.h"

/*---------------------------------------------------------------------------
 *	Constants
\*--------------------------------------------------------------------------*/

#define LOG

#define XA_FADE 8
#define XA_FREQ 8
#define NUM_XA_FILES 16

/*---------------------------------------------------------------------------
 *	Externals
\*--------------------------------------------------------------------------*/

#if defined(PSX_SONY_DEMO_SHELL)
extern int GnCDDATrack;
#endif

extern UBYTE CurrentAtmosphere;
extern UBYTE IsAtmospherePlaying;

/*---------------------------------------------------------------------------
 *	Locals
\*--------------------------------------------------------------------------*/

#if defined(PLAY_CDTRACKS)

volatile char XAFlag;	// XA control status
volatile char XAWait;

char XAPlaying;		// playing any XA audio ?
WORD XAMasterVolume;	// master XA volume
volatile WORD XAVolume;			// current XA volume
volatile WORD XAReqVolume;		// required XA volume
WORD XATrack;			// current XA track
WORD XAReqTrack;		// required XA track
WORD XAFadeRate = 8;

static char XARepeat;
static int XAStartPos;
int XACurPos;
int XAEndPos;
static int XATrackList[NUM_XA_FILES][2];


uint16 XATrackClip[NUM_XA_FILES*8] = {
	 0xfe00, 0xf480, 0xef00, 0xeb00, 0xeb00, 0xea00, 0xdc00, 0xc800,	// 2-9
	      0, 0xf800, 0xf800, 0xf800, 0xf180, 0xf100, 0xee00, 0xee00,	// 10-17
	      0,      0, 0xfd80, 0xfc80, 0xfc80, 0xfc00, 0xfc00, 0xfb80,	// 18-25
	 0xffc0, 0xf180, 0xe700, 0xcf00, 0xcb00, 0xca00, 0xc2d0, 0xc000,	// 26-33
	 0xffc0, 0xff00, 0xf700, 0xeca0, 0xd580, 0xd440, 0xd440, 0xd400,	// 34-41
	 0xff80, 0xfda0, 0xfd00, 0xfd00, 0xfd80, 0xfd00, 0xfc80, 0xfc80,	// 42-49
	 0xff80, 0xff80, 0xff80, 0xfe00, 0xfd80, 0xfc80, 0xfd00, 0xfd00,	// 50-57
	 0xb100, 0xb000, 0xb180, 0xb180, 0xff80, 0xfe80, 0xf000, 0xea00,	// 58-65
	 0xea00, 0xe700, 0xe700, 0xd580, 0xcde0, 0xcb00, 0xc900, 0xffe0,	// 66-73
	 0xff00, 0xfe80, 0xe200, 0xd780, 	   0,	 0,	    0,	  0,	// 74-81
#ifdef ENGLISH
	 0xff60, 0xff60, 0xffe0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0,	// 82-89
	 0xff00, 0xff00, 0xff10, 0xff40, 0xff80, 0xffc0, 0xffc0, 0xffc8,	// 90-97
	 0xff00, 0xff20, 0xff20, 0xff80, 0xff80, 0xff80, 0xffa0, 0xffd0,	// 98-105
	 0xfb80, 0xfb80, 0xfa80, 0xfe00, 0xfe00, 0xff00, 0xff00,	  0,	// 106-113
	 0xfe00, 0xfe00, 0xfe00, 0xfe80, 0xff00, 0xffe0,	    0, 0xcd00,	// 114-121
#endif
#ifdef FRENCH
	 0xff60, 0xff60, 0xffc0, 0xffe0, 0xffc0, 0xffc0, 0xffc0, 0xffc0,	// 82-89
	 0xff00, 0xff00, 0xff10, 0xff60, 0xff80, 0xffc0, 0xffd0, 0xffc8,	// 90-97
	 0xff00, 0xff20, 0xff20, 0xff80, 0xff80, 0xff80, 0xffd0, 0xffc0,	// 98-105
	 0xfc80, 0xfb80, 0xfd00, 0xfe00, 0xfe00, 0xff00, 0xff00, 0xffc0,	// 106-113
	 0xa800, 0xa800, 0xa600, 0xa800, 0xa800, 0xaa80, 0xff80, 0xcd00,	// 114-121
#endif
#ifdef GERMAN
	 0xff60, 0xff60, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0,	// 82-89
	 0xff00, 0xff00, 0xff10, 0xff40, 0xff80, 0xffc0, 0xffc0, 0xffc8,	// 90-97
	 0xff00, 0xfe00, 0xfe00, 0xfe00, 0xfe00, 0xfe00, 0xfe40, 0xfe40,	// 98-105
	 0xfac0, 0xfa80, 0xfb80, 0xfd00, 0xfc80, 0xfe00, 0xffe0, 0xff80,	// 106-113
	 0xab80, 0xa700, 0xa780, 0xa940, 0xa980, 0xa9c0, 0xfff0, 0xcd00,	// 114-121
#endif
#ifdef ITALIAN
	 0xff60, 0xff60, 0xff60, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0,	// 82-89
	 0xff00, 0xff00, 0xff10, 0xff40, 0xff80, 0xff20, 0xffd0, 0xffc0,	// 90-97
	 0xffe0, 0xff20, 0xff40, 0xff80, 0xffc0, 0xfec0, 0xffc0, 0xffc0,	// 98-105
	 0xfac0, 0xfa80, 0xfe00, 0xfd80, 0xfd40, 0xfe80, 0xff00, 0xffc0,	// 106-113
	 0xa800, 0xa740, 0xa900, 0xa900, 0xa900, 0xaa00, 0xfff0, 0xcd00,	// 114-121
#endif
#ifdef SPANISH
	 0xff60, 0xff60, 0xff80, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0,	// 82-89
	 0xff00, 0xff00, 0xff10, 0xff40, 0xff00, 0xffa0, 0xffd0, 0xff80,	// 90-97
	 0xff00, 0xff00, 0xfec0, 0xff80, 0xff40, 0xff40, 0xffc0, 0xffc0,	// 98-105
	 0xfac0, 0xf940, 0xfc00, 0xfc00, 0xfe00, 0xfe00, 0xfe00, 0xffc8,	// 106-113
	 0xaa00, 0xabc0, 0xaac0, 0xad00, 0xab80, 0xac80, 0xfff0, 0xcd00,	// 114-121
#endif
	 0xfe00,	    0,	  0,		0,	   0,	 0,	    0,	  0	// 122
};

#endif

#if defined(CD_LOAD)

static uint8* LpSectorBuffer;
static uint32 LadwSectorBuffer[512];

static int LnCurSector;
static int LnBufferPos;
static int LnStartSector;
static int LnFileSectors;
static int LnSectorBufferSize;
static int LnSectorBufferSectors;

#endif

/*---------------------------------------------------------------------------
 *	Macros
\*--------------------------------------------------------------------------*/

#ifdef LOG
#define Log_(format,args...) printf(format "\n" , ## args)
#else
#define Log_(args...)
#endif

/*---------------------------------------------------------------------------
 *	Local Functions
\*--------------------------------------------------------------------------*/

#ifdef CD_LOAD

static void MakeCDFileName(char* szDest,char* szSource)
{
	char c;

	while ((c=*szSource++))
	{
		if ((c>='a') && (c<='z'))
			c&=0xdf;
		*szDest++=c;
	}

	szDest[0]=';';
	szDest[1]='1';
	szDest[2]=0;
}


#endif

#if defined(PLAY_CDTRACKS)

/****************************************************************************
*
* SYNOPSIS
*
* PURPOSE
*
* INPUTS
*
* RETURN
*/
static void XAReplay()
{
	CdlLOC loc;

	CdIntToPos(XAStartPos, &loc);

	if (CdControl(CdlReadS, (UBYTE *)&loc, 0) == 1)
		XACurPos = XAStartPos;
}

/****************************************************************************
*
* SYNOPSIS
*
* PURPOSE
*
* INPUTS
*
* RETURN
*/
static void cbvsync()
{
	int ret;
	UBYTE io[8];

/* -------- process track switching */

	switch (XAFlag)
	{
	/* -------------- */
		case 1:			// fade out current track
	/* -------------- */
			if (!XAVolume)
				XAFlag++;
			break;
	/* -------------- */
		case 2:			// initiate new track
	/* -------------- */
			XATrack	 = XAReqTrack;
			XAStartPos = XATrackList[XATrack/8][0];
			XAEndPos   = XATrackList[XATrack/8][1] + (sint16)XATrackClip[XATrack];
			XACurPos   = XAStartPos;

			io[0] = 1;
			io[1] = XATrack&7;
			CdControlF(CdlSetfilter, &io[0]);
			XAFlag++;
			break;
	/* -------------- */
		case 3:
	/* -------------- */
			XAReplay();	// seek to new track
			XAReqVolume = XAMasterVolume;
			XAFlag++;
			break;
	/* -------------- */
		case 4:			// fade in new track
	/* -------------- */
			if (XAVolume == XAMasterVolume)
			{
				XAFlag++;
				XAWait = 60;
			}
			break;
	/* -------------- */
		case 5:
	/* -------------- */
			if (!XAWait)
				XAFlag++;
			XAWait--;
			break;
	/* -------------- */
		case 6:			// process track
	/* -------------- */
			if ((VSync(-1) % XA_FREQ) == 0)
			{
				if ((ret = CdSync(1, io)) == CdlDiskError)
					XAReplay();

				else if (ret == CdlComplete)
				{
					if (XACurPos >= XAEndPos)
					{
						if (XARepeat)
							XAReplay();
						else
						{
							if (!CurrentAtmosphere)
							{
								CdControlB(CdlPause, 0, 0);
								XAFlag = 7;
							}
							else
							{
								XAVolume = 0;
								XAReqTrack = CurrentAtmosphere-2;
								XARepeat = 1;
								XAFlag = 2;
								CDDA_SetVolume(0);
							}
						}
					}

					else
					{
						int cnt;

						if ((CdLastCom() == CdlGetlocP)
						&& ((cnt = CdPosToInt((CdlLOC *)&io[5])) > 0))
							XACurPos = cnt;

						CdControlF(CdlGetlocP, 0);
					}
				}
			}
			break;
	/* -------------- */
		case 7:
	/* -------------- */
			break;
	}

/* -------- handle volume fades */

	if (XAVolume < XAReqVolume)
	{
		if ((XAVolume += XAFadeRate) >= XAReqVolume)
			XAVolume = XAReqVolume;

		CDDA_SetVolume(XAVolume);
	}

	else if (XAVolume > XAReqVolume)
	{
		if ((XAVolume -= XAFadeRate) <= XAReqVolume)
			XAVolume = XAReqVolume;

		CDDA_SetVolume(XAVolume);
	}
}

#endif

/*---------------------------------------------------------------------------
 *	Public Functions
\*--------------------------------------------------------------------------*/

/****************************************************************************
*
* SYNOPSIS
*
* PURPOSE
*
* INPUTS
*
* RETURN
*/
void S_CDPlay(sint16 track, int mode)
{
#if defined(PLAY_CDTRACKS)
	UBYTE param[4];

	Log_(__FUNCTION__);

/* -------- ensure CD is initialised for XA playback */

	if (XATrack == -1)
	{
//		printf("!");
		param[0] = CdlModeSpeed|CdlModeRT|CdlModeSF;
		CdControlB(CdlSetmode, &param[0], 0);
		VSync(3);

		CdControlB(CdlPause, 0, 0);
	}


	track -= 2;

	if ((XATrack == track)
	||  (XAReqTrack == track))
		return;

	XAReqTrack = track;
	XARepeat = mode;

	if (XAFlag)
	{
		XAFlag = 1;
		XAReqVolume = 0;
	}
	else
	{
		XAFlag = 2;
	}

#if 0

	param[0] = CdlModeSpeed|CdlModeRT|CdlModeSF;
	CdControlB(CdlSetmode, &param[0], 0);
	VSync(3);

	XAPlaying = 0;

	CdControlF(CdlPause, 0);

/* -------- determine track */

	XATrack = track;

	XAStartPos = XATrackList[XATrack/8][0];
	XAEndPos   = XATrackList[XATrack/8][1] + (sint16)XATrackClip[XATrack];
	XACurPos   = XAStartPos;

/* -------- set audio filter */

	param[0] = 1;
	param[1] = XATrack&7;
	CdControlB(CdlSetfilter, &param[0], 0);

	XAPlaying = 1;
	XARepeat = mode;
	XAReplay(CdlReadS);
#endif
#endif
}
/****************************************************************************
*
* SYNOPSIS
*
* PURPOSE
*
* INPUTS
*
* RETURN
*/
void S_CDStop()
{
#if defined(PLAY_CDTRACKS)
	UBYTE param[4];

	Log_(__FUNCTION__);

	XAFlag = 0;
//	CdControlF(CdlPause, 0, 0);
	CdControlB(CdlPause, 0, 0);

	XATrack = XAReqTrack = -1;

	param[0] = CdlModeSpeed;
	CdControlB(CdlSetmode, &param[0], 0);
	VSync(3);
#endif
}
/****************************************************************************
*
* SYNOPSIS
*
* PURPOSE
*
* INPUTS
*
* RETURN
*/
void S_CDPause()
{
#if defined(PLAY_CDTRACKS)
	Log_(__FUNCTION__);

	if (XATrack < 0)
		return;

//	CdControlF(CdlPause, 0, 0);
	CdControlF(CdlPause, 0);
#endif
}
/****************************************************************************
*
* SYNOPSIS
*
* PURPOSE
*
* INPUTS
*
* RETURN
*/
void S_CDRestart()
{
#if defined(PLAY_CDTRACKS)
	Log_(__FUNCTION__);

	if ((XATrack < 0) || (XAFlag == 7))
		return;

//	CdControlF(CdlReadS, 0, 0);
	CdControlF(CdlReadS, 0);
#endif
}
/****************************************************************************
*
* SYNOPSIS
*
* PURPOSE
*
* INPUTS
*
* RETURN
*/
void S_StartSyncedAudio(int nTrack)
{
	int i;
#if defined(PLAY_CDTRACKS)
	CurrentAtmosphere = 0;
	IsAtmospherePlaying = 1;
	S_CDPlay(nTrack, 0);
	while(XAFlag < 4)
		i++;
#endif

#if 0
//##	if ((gameflow.demoversion)
//##	&&  (nTrack != gameflow.title_track))
//##		return;

	cdtrack = nTrack;
//	nTrack = GetRealTrack(nTrack);

	Log_(__FUNCTION__);

	CDDA_Play(nTrack|CD_PLAY_ONCE);
	CDDA_Sync(CD_SYNC_WAIT_START);
#endif
}

/****************************************************************************
*
* SYNOPSIS
*
* PURPOSE
*
* INPUTS
*
* RETURN
*/
int CDDA_SetMasterVolume(int nVolume)
{
#if defined(PLAY_CDTRACKS)
	int nCurVolume;

//	Log_(__FUNCTION__);

	XAMasterVolume = nVolume;
	CDDA_SetVolume(nVolume);

	return nCurVolume;
#endif
}

void CDDA_SetVolume(int nVolume)
{
#if defined(PLAY_CDTRACKS)
	SpuCommonAttr attr;

	attr.mask = SPU_COMMON_CDVOLL | SPU_COMMON_CDVOLR | SPU_COMMON_CDMIX;

	attr.cd.volume.left = nVolume << 6;
	attr.cd.volume.right = nVolume << 6;
	attr.cd.mix = SPU_ON;
	SpuSetCommonAttr(&attr);
#endif
}

int CDDA_GetCurrentLoc(void)
{
#ifdef PLAY_CDTRACKS
	return (XACurPos - XAStartPos) / 8;
#else
	return 0;
#endif
}

/****************************************************************************
*
* SYNOPSIS
*
* PURPOSE
*
* INPUTS
*
* RETURN
*/
void CD_Init(int state)
{
#if defined(PLAY_CDTRACKS) || defined(CD_LOAD)
	int i;
	UBYTE param[4];

	Log_(__FUNCTION__);

	param[0] = CdlModeSpeed;
	CdControlB(CdlSetmode, &param[0], 0);
	VSync(3);

#if defined(CD_LOAD)
	CDFS_UseSectorBuffer(0, 0);
#endif

#if defined(PLAY_CDTRACKS)

	if (state)
	{
		for ( i=0 ; i<NUM_XA_FILES ; i++ )
		{
			CdlFILE fp;
			char buf[80];

			sprintf(buf, "\\XA\\F%d.XA;1", i+1);

			if (!CdSearchFile(&fp, &buf[0]))
				printf("WARNING : Can't find %s\n", buf);
			else
			{
				XATrackList[i][0] = CdPosToInt(&fp.pos);
				XATrackList[i][1] = XATrackList[i][0] + (((fp.size+2047)/2048));
			}
		}

		XAFlag = 0;
		XAVolume = 0;
		XATrack = -1;
	//	XAPlaying = 0;
		VSYNC_Add(cbvsync);
	}

#endif
#endif
}


/*** Filing system stuff ***/

#if defined(CD_LOAD)

void CDFS_UseSectorBuffer(uint32* pBuffer,int nSize)
{
	Log_(__FUNCTION__);

	if (pBuffer)
	{
		LpSectorBuffer = (uint8*)pBuffer;
		LnSectorBufferSectors = nSize>>11;
	}
	else
	{
		LpSectorBuffer = (uint8*)LadwSectorBuffer;
		LnSectorBufferSectors = 1;
	}

	LnSectorBufferSize = LnSectorBufferSectors<<11;
}

int CDFS_OpenFile(char* szFileName)
{
	CdlFILE fp;
	UBYTE param[4];
	char szNewFileName[64];

	param[0] = CdlModeSpeed;
	CdControlB(CdlSetmode, &param[0], 0);
	VSync(3);

	MakeCDFileName(szNewFileName,szFileName);

	if (!CdSearchFile(&fp, szNewFileName))
	{
		Log_("Can't find file %s",szNewFileName);
		return -1;
	}

	LnStartSector = CdPosToInt(&fp.pos);
	LnCurSector = 0;
	LnBufferPos = LnSectorBufferSize;
	LnFileSectors = (fp.size+2047)>>11;

	return fp.size;
}

void CDFS_GetSectors(int nFirstSector,int nSectors,void* pDest)
{
	int nFlag;
	CdlLOC pos;

	CdIntToPos(LnStartSector+nFirstSector, &pos);

	do {
		CdControlB(CdlSetloc, (char*)&pos, 0);

		CdRead(nSectors, pDest, CdlModeSpeed);

		while ((nFlag = CdReadSync(1, 0)) > 0)
			VSync(0);
	} while (nFlag<0);

	LOAD_Update(nSectors<<11);
}

int CDFS_LoadFile(char *szFileName, void *pDest)
{
	int nSize, nSectors, nExtra;

	Log_(__FUNCTION__);

	if ((nSize = CDFS_OpenFile(szFileName)) < 0)
		return -1;

	nSectors = nSize>>11;
	CDFS_GetSectors(0, nSectors, pDest);

	if ((nExtra = nSize&0x7ff))
	{
		CDFS_GetSectors(nSectors, 1, LadwSectorBuffer);
		S_MemCpy(((char*)pDest)+(nSectors<<11), LadwSectorBuffer, nExtra);
	}
	return nSize;
}

int CDFS_GetFileSize(char* szFileName)
{
	Log_(__FUNCTION__);

	return CDFS_OpenFile(szFileName);
}

int CDFS_ReadBytes(void* pvDest,int nBytes)
{
	uint8* pbDest;
	int nBytesLeft, nBytesToCopy, nSectorsToRead;

	pbDest = (uint8*)pvDest;
	nBytesLeft = nBytes;

	while (nBytesLeft)
	{
		if (LnBufferPos == LnSectorBufferSize)
		{
			nSectorsToRead = LnFileSectors-LnCurSector;

			if (LnSectorBufferSectors < nSectorsToRead)
				nSectorsToRead = LnSectorBufferSectors;

			CDFS_GetSectors(LnCurSector, nSectorsToRead, LpSectorBuffer);
			LnCurSector += nSectorsToRead;
			LnBufferPos = 0;
		}

		nBytesToCopy = LnSectorBufferSize-LnBufferPos;

		if (nBytesLeft < nBytesToCopy)
			nBytesToCopy = nBytesLeft;

		S_MemCpy(pbDest, LpSectorBuffer+LnBufferPos, nBytesToCopy);

		pbDest+=nBytesToCopy;
		LnBufferPos+=nBytesToCopy;
		nBytesLeft-=nBytesToCopy;
	}
	return nBytes;
}

#endif
