// THIS MODULE IS TEMPORARY - USED FOR OUR SOUND SYSTEM INTIL IT IS IMPLEMENTED FOR THE SGP
// TAKEN FROM MS SAMPLES FOR DirectSound

#ifdef PRECOMPILEDHEADERS
	#include "Utils All.h"
	#include <windows.h>
	#include <mmsystem.h>
	#include <dsound.h>
#else
	#include "types.h"
	#include <windows.h>
	#include <mmsystem.h>
	#include <dsound.h>
#endif

#define WIN32_LEAN_AND_MEAN

typedef struct
{
	BYTE *pbWaveData;				// pointer into wave resource (for restore)
	DWORD cbWaveSize;				// size of wave data (for restore)
	int iAlloc;					 // number of buffers.
	int iCurrent;					// current buffer
	IDirectSoundBuffer* Buffers[1]; // list of buffers

} SNDOBJ, *HSNDOBJ;

#define _HSNDOBJ_DEFINED
#include "dsutil.h"

static const char c_szWAV[] = "WAVE";

///////////////////////////////////////////////////////////////////////////////
//
// DSLoadSoundBuffer
//
///////////////////////////////////////////////////////////////////////////////

IDirectSoundBuffer *DSLoadSoundBuffer(IDirectSound *pDS, LPCTSTR lpName)
{
	IDirectSoundBuffer *pDSB = NULL;
	DSBUFFERDESC dsBD = {0};
	BYTE *pbWaveData;

	if (DSGetWaveResource(NULL, lpName, &dsBD.lpwfxFormat, &pbWaveData, &dsBD.dwBufferBytes))
	{
		dsBD.dwSize = sizeof(dsBD);
		dsBD.dwFlags = DSBCAPS_STATIC | DSBCAPS_CTRLDEFAULT; // | DSBCAPS_GETCURRENTPOSITION2;

		if (SUCCEEDED(IDirectSound_CreateSoundBuffer(pDS, &dsBD, &pDSB, NULL)))
		{
			if (!DSFillSoundBuffer(pDSB, pbWaveData, dsBD.dwBufferBytes))
			{
				IDirectSoundBuffer_Release(pDSB);
				pDSB = NULL;
			}
		}
		else
		{
			pDSB = NULL;
		}
	}

	return pDSB;
}

///////////////////////////////////////////////////////////////////////////////
//
// DSReloadSoundBuffer
//
///////////////////////////////////////////////////////////////////////////////

BOOL DSReloadSoundBuffer(IDirectSoundBuffer *pDSB, LPCTSTR lpName)
{
	BOOL result=FALSE;
	BYTE *pbWaveData;
	DWORD cbWaveSize;

	if (DSGetWaveResource(NULL, lpName, NULL, &pbWaveData, &cbWaveSize))
	{
		if (SUCCEEDED(IDirectSoundBuffer_Restore(pDSB)) &&
			DSFillSoundBuffer(pDSB, pbWaveData, cbWaveSize))
		{
			result = TRUE;
		}
	}

	return result;
}

///////////////////////////////////////////////////////////////////////////////
//
// DSGetWaveResource
//
///////////////////////////////////////////////////////////////////////////////

BOOL DSGetWaveResource(HMODULE hModule, LPCTSTR lpName,
	WAVEFORMATEX **ppWaveHeader, BYTE **ppbWaveData, DWORD *pcbWaveSize)
{
	HRSRC hResInfo;
	HGLOBAL hResData;
	void *pvRes;

	if (((hResInfo = FindResource(hModule, lpName, c_szWAV)) != NULL) &&
		((hResData = LoadResource(hModule, hResInfo)) != NULL) &&
		((pvRes = LockResource(hResData)) != NULL) &&
		DSParseWaveResource(pvRes, ppWaveHeader, ppbWaveData, pcbWaveSize))
	{
		return TRUE;
	}

	return FALSE;
}

///////////////////////////////////////////////////////////////////////////////
// SndObj fns
///////////////////////////////////////////////////////////////////////////////

SNDOBJ *SndObjCreate(IDirectSound *pDS, LPCTSTR lpName, int iConcurrent)
{
	SNDOBJ *pSO = NULL;
	LPWAVEFORMATEX pWaveHeader;
	BYTE *pbData;
	UINT cbData;

	if (DSGetWaveResource(NULL, lpName, &pWaveHeader, &pbData, (DWORD *)&cbData))
	{
		if (iConcurrent < 1)
			iConcurrent = 1;

		if ((pSO = (SNDOBJ *)LocalAlloc(LPTR, sizeof(SNDOBJ) +
			(iConcurrent-1) * sizeof(IDirectSoundBuffer *))) != NULL)
		{
			int i;

			pSO->iAlloc = iConcurrent;
			pSO->pbWaveData = pbData;
			pSO->cbWaveSize = cbData;
			pSO->Buffers[0] = DSLoadSoundBuffer(pDS, lpName);

			for (i=1; i<pSO->iAlloc; i++)
			{
				if (FAILED(IDirectSound_DuplicateSoundBuffer(pDS,
					pSO->Buffers[0], &pSO->Buffers[i])))
				{
					pSO->Buffers[i] = DSLoadSoundBuffer(pDS, lpName);
					if (!pSO->Buffers[i]) {
						SndObjDestroy(pSO);
						pSO = NULL;
						break;
					}
				}
			}
		}
	}

	return pSO;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

void SndObjDestroy(SNDOBJ *pSO)
{
	if (pSO)
	{
		int i;

		for (i=0; i<pSO->iAlloc; i++)
		{
			if (pSO->Buffers[i])
			{
				IDirectSoundBuffer_Release(pSO->Buffers[i]);
				pSO->Buffers[i] = NULL;
			}
		}
		LocalFree((HANDLE)pSO);
	}
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

IDirectSoundBuffer *SndObjGetFreeBuffer(SNDOBJ *pSO)
{
	IDirectSoundBuffer *pDSB;

	if (pSO == NULL)
		return NULL;

	if (pDSB = pSO->Buffers[pSO->iCurrent])
	{
		HRESULT hres;
		DWORD dwStatus;

		hres = IDirectSoundBuffer_GetStatus(pDSB, &dwStatus);

		if (FAILED(hres))
			dwStatus = 0;

		if ((dwStatus & DSBSTATUS_PLAYING) == DSBSTATUS_PLAYING)
		{
			if (pSO->iAlloc > 1)
			{
				if (++pSO->iCurrent >= pSO->iAlloc)
					pSO->iCurrent = 0;

				pDSB = pSO->Buffers[pSO->iCurrent];
				hres = IDirectSoundBuffer_GetStatus(pDSB, &dwStatus);

				if (SUCCEEDED(hres) && (dwStatus & DSBSTATUS_PLAYING) == DSBSTATUS_PLAYING)
				{
					IDirectSoundBuffer_Stop(pDSB);
					IDirectSoundBuffer_SetCurrentPosition(pDSB, 0);
				}
			}
			else
			{
				pDSB = NULL;
			}
		}

		if (pDSB && (dwStatus & DSBSTATUS_BUFFERLOST))
		{
			if (FAILED(IDirectSoundBuffer_Restore(pDSB)) ||
				!DSFillSoundBuffer(pDSB, pSO->pbWaveData, pSO->cbWaveSize))
			{
				pDSB = NULL;
			}
		}
	}

	return pDSB;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

BOOL SndObjPlay(SNDOBJ *pSO, DWORD dwPlayFlags)
{
	BOOL result = FALSE;

	if (pSO == NULL)
		return FALSE;

	if ((!(dwPlayFlags & DSBPLAY_LOOPING) || (pSO->iAlloc == 1)))
	{
		IDirectSoundBuffer *pDSB = SndObjGetFreeBuffer(pSO);
		if (pDSB != NULL) {
			result = SUCCEEDED(IDirectSoundBuffer_Play(pDSB, 0, 0, dwPlayFlags));
		}
	}

	return result;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

BOOL SndObjStop(SNDOBJ *pSO)
{
	int i;

	if (pSO == NULL)
		return FALSE;

	for (i=0; i<pSO->iAlloc; i++)
	{
		IDirectSoundBuffer_Stop(pSO->Buffers[i]);
		IDirectSoundBuffer_SetCurrentPosition(pSO->Buffers[i], 0);
	}

	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

BOOL DSFillSoundBuffer(IDirectSoundBuffer *pDSB, BYTE *pbWaveData, DWORD cbWaveSize)
{
	if (pDSB && pbWaveData && cbWaveSize)
	{
		LPVOID pMem1, pMem2;
		DWORD dwSize1, dwSize2;

		if (SUCCEEDED(IDirectSoundBuffer_Lock(pDSB, 0, cbWaveSize,
			&pMem1, &dwSize1, &pMem2, &dwSize2, 0)))
		{
			CopyMemory(pMem1, pbWaveData, dwSize1);

			if ( 0 != dwSize2 )
				CopyMemory(pMem2, pbWaveData+dwSize1, dwSize2);

			IDirectSoundBuffer_Unlock(pDSB, pMem1, dwSize1, pMem2, dwSize2);
			return TRUE;
		}
	}

	return FALSE;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

BOOL DSParseWaveResource(void *pvRes, WAVEFORMATEX **ppWaveHeader, BYTE **ppbWaveData,DWORD *pcbWaveSize)
{
	DWORD *pdw;
	DWORD *pdwEnd;
	DWORD dwRiff;
	DWORD dwType;
	DWORD dwLength;

	if (ppWaveHeader)
		*ppWaveHeader = NULL;

	if (ppbWaveData)
		*ppbWaveData = NULL;

	if (pcbWaveSize)
		*pcbWaveSize = 0;

	pdw = (DWORD *)pvRes;
	dwRiff = *pdw++;
	dwLength = *pdw++;
	dwType = *pdw++;

	if (dwRiff != mmioFOURCC('R', 'I', 'F', 'F'))
		goto exit;		// not even RIFF

	if (dwType != mmioFOURCC('W', 'A', 'V', 'E'))
		goto exit;		// not a WAV

	pdwEnd = (DWORD *)((BYTE *)pdw + dwLength-4);

	while (pdw < pdwEnd)
	{
		dwType = *pdw++;
		dwLength = *pdw++;

		switch (dwType)
		{
		case mmioFOURCC('f', 'm', 't', ' '):
			if (ppWaveHeader && !*ppWaveHeader)
			{
				if (dwLength < sizeof(WAVEFORMAT))
					goto exit;		// not a WAV

				*ppWaveHeader = (WAVEFORMATEX *)pdw;

				if ((!ppbWaveData || *ppbWaveData) &&
					(!pcbWaveSize || *pcbWaveSize))
				{
					return TRUE;
				}
			}
			break;

		case mmioFOURCC('d', 'a', 't', 'a'):
			if ((ppbWaveData && !*ppbWaveData) ||
				(pcbWaveSize && !*pcbWaveSize))
			{
				if (ppbWaveData)
					*ppbWaveData = (LPBYTE)pdw;

				if (pcbWaveSize)
					*pcbWaveSize = dwLength;

				if (!ppWaveHeader || *ppWaveHeader)
					return TRUE;
			}
			break;
		}

		pdw = (DWORD *)((BYTE *)pdw + ((dwLength+1)&~1));
	}

exit:
	return FALSE;
}
