/*
// ================================================================
// BitStream.cpp
// Ubi Sound Studios
// 18/03/1997
// Guillaume Michel
// ================================================================
*/

#include "SNDinc.h"

#if defined(_DLL_COMPILATION_MODE)
#include "snddll.h"
#endif

#include "bitstrm.h"

// Size of the buffer (BYTES)
#define kSizeOfBuffer 4096
// Synch word
#define kSynchWord 0xfff

//-------------------------------------------------------------------------
// Description : Initialise one bit stream
//-------------------------------------------------------------------------
// 
//-------------------------------------------------------------------------
// Input  : bitstream pointer
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bInitBitStream(SND_tdstBitStream* pBitStream)
{
	pBitStream->pFile = OPENFILESND_FAILED;
	pBitStream->pucCurrentPointer = NULL;
	pBitStream->ulBytePos = 0;
	pBitStream->ucBitPos = 0;
	pBitStream->ulLastRead = 0;
	pBitStream->ucCurrentByte = 0;
	pBitStream->ulSize = 0;
	pBitStream->ulDataOffset = 0;
	pBitStream->pucInputBuffer = NULL;
	pBitStream->ulFinalSize = 0;
	pBitStream->ulDolbyMark = 0;

	return TRUE;
}

//-------------------------------------------------------------------------
// Description : allocate memory for one bitstream
//-------------------------------------------------------------------------
// ALLOCATE MEMORY
//-------------------------------------------------------------------------
// Input  : bitstream pointer
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 24/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bAllocateBitStream(SND_tdstBitStream* pBitStream)
{
	snd_assert(pBitStream);
	// Allocate memory once at the begining of the program.
	pBitStream->pucBuffer = (unsigned char*) SND_fn_pvMallocSnd(kSizeOfBuffer);
	return ((pBitStream->pucBuffer) != NULL);
}

//-------------------------------------------------------------------------
// Description : free one bit stream
//-------------------------------------------------------------------------
// DO FREE MEMORY !!!
//-------------------------------------------------------------------------
// Input  : bitstream pointer
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 24/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bFreeBitStream(SND_tdstBitStream* pBitStream)
{
	SND_fn_bCloseBitStream(pBitStream);
	if (pBitStream->pucBuffer) SND_fn_vFreeSnd(pBitStream->pucBuffer);
	pBitStream->pucBuffer = NULL;
	return TRUE;
}

//-------------------------------------------------------------------------
// Description : Open a new bitstream
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : filename and bitstream pointer
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bOpenBitStreamFile(SND_tdstBitStream* pBitStream, char* pFilename, unsigned long ulOffset)
{
	char tmp[256];

	snd_assert(pBitStream);
	
	SND_fn_bInitBitStream(pBitStream);
    SND_fn_vResolveFileName(pFilename,tmp);
	pBitStream->pFile = SND_fn_hOpenFileReadSnd(tmp);
	
	if (pBitStream->pFile==OPENFILESND_FAILED)
	{
		SND_fn_vDisplayError(E_uwSndCannotOpenFile,tmp);
		return FALSE;
	}

	snd_assert(pBitStream->pFile);
	SND_fn_dwSeekFileSnd(pBitStream->pFile,ulOffset,SEEKFILESND_BEGIN);

	pBitStream->pucCurrentPointer = pBitStream->pucBuffer;
	SND_fn_dwReadFileSnd(pBitStream->pFile,sizeof(int),&(pBitStream->ulFinalSize));
	SND_fn_dwReadFileSnd(pBitStream->pFile,sizeof(int),&(pBitStream->ulDolbyMark));
	if ((pBitStream->ulDolbyMark == 'SUR1') || (pBitStream->ulDolbyMark == 'SUR2')){
	} else {
		// on revient en arrière de 4 octets
		SND_fn_dwSeekFileSnd(pBitStream->pFile,- 4,SEEKFILESND_CURRENT);
	}

	return SND_fn_bFillBuffer(pBitStream);
}

//-------------------------------------------------------------------------
// Description : Open a new bitstream
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : bitstream pointer and buffer and size
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bOpenBitStreamMem(SND_tdstBitStream* pBitStream, unsigned char* pBuffer, unsigned long ulSize)
{
	snd_assert(pBitStream);

	SND_fn_bInitBitStream(pBitStream);
	pBitStream->ulSize = ulSize;
	pBitStream->pucInputBuffer = pBuffer;
	pBitStream->pucCurrentPointer = pBuffer;
	pBitStream->ulFinalSize = *((long*)(pBitStream->pucCurrentPointer));
	pBitStream->pucCurrentPointer += 4;
	pBitStream->ulDolbyMark = *((long*)(pBitStream->pucCurrentPointer));
	if ((pBitStream->ulDolbyMark == 'SUR1') || (pBitStream->ulDolbyMark == 'SUR2')){
		pBitStream->pucCurrentPointer += 4;
	}
	return SND_fn_bFillBuffer(pBitStream);
}

//-------------------------------------------------------------------------
// Description : Free a bitstream
//-------------------------------------------------------------------------
// DOES NOT FREE MEMORY !!!!
//-------------------------------------------------------------------------
// Input  : bitstream pointer
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bCloseBitStream(SND_tdstBitStream* pBitStream)
{
	snd_assert(pBitStream);

	if (pBitStream->pFile!=OPENFILESND_FAILED)	{
		SND_fn_vCloseFileSnd(pBitStream->pFile);
	}

	SND_fn_bInitBitStream(pBitStream);
	return TRUE;

}

//-------------------------------------------------------------------------
// Description : read one bit from the bitstream
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : bitstream pointer
//			pointer on value we want to fill
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bGet1Bit(SND_tdstBitStream* pBitStream, unsigned short *puwValue)
{
	snd_assert(pBitStream);
	if (pBitStream->ucBitPos == 8) {
		// We must change current byte in the buffer
		pBitStream->ulBytePos++;
		pBitStream->pucCurrentPointer++;

		if (pBitStream->ulBytePos == pBitStream->ulLastRead) {
			// We must load a new buffer	
			if (pBitStream->ulLastRead != kSizeOfBuffer) 
				return FALSE;	// We have found the end of the file

			// Load a new buffer
			if(!SND_fn_bFillBuffer(pBitStream))
				return FALSE;	// We have found the end of the file

		}

		// Change the current byte
		pBitStream->ucCurrentByte = *pBitStream->pucCurrentPointer;
		pBitStream->ucBitPos = 0;
	}

	// Read the bit
	*puwValue = ((unsigned char)((pBitStream->ucCurrentByte) & 0x80)) >> 7;
	pBitStream->ucBitPos++;
	pBitStream->ucCurrentByte <<= 1;

	return TRUE;

}

//-------------------------------------------------------------------------
// Description : read many bits from the bitstream
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : bitstream pointer
//			pointer on value we want to fill
//			number of bits we want to read... ( <= 16)
// Output : SndBool
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bGetNBits(SND_tdstBitStream* pBitStream, unsigned short *puwValue, unsigned char ucNbBits)
{
	unsigned char j,tmp;
	unsigned short i;

	snd_assert(pBitStream);
	if (pBitStream->ucBitPos == 8) {
		// We must change current byte in the buffer
		pBitStream->ulBytePos++;
		pBitStream->pucCurrentPointer++;

		if (pBitStream->ulBytePos == pBitStream->ulLastRead) {
			// We must load a new buffer	
			if (pBitStream->ulLastRead != kSizeOfBuffer) 
				return FALSE;	// We have found the end of the file

			// Load a new buffer
			if(!SND_fn_bFillBuffer(pBitStream))
				return FALSE;	// We have found the end of the file

		}

		// Change the current byte
		pBitStream->ucCurrentByte = *pBitStream->pucCurrentPointer;
		pBitStream->ucBitPos = 0;
	}

	if (ucNbBits > (j = 8 - pBitStream->ucBitPos)) {
		// We can't read the N bits...
		tmp = (pBitStream->ucCurrentByte) >> pBitStream->ucBitPos;
		pBitStream->ucBitPos = 8;
		if (SND_fn_bGetNBits(pBitStream,&i,(unsigned char)(ucNbBits - j))) {
			*puwValue = (tmp << (ucNbBits - j)) | i;
			return TRUE; 
		} else return FALSE;

	} else {
		// We can read the N bits now...
		*puwValue = (pBitStream->ucCurrentByte) >> ( 8 - ucNbBits);
		pBitStream->ucBitPos += ucNbBits;
		pBitStream->ucCurrentByte <<= ucNbBits;
	}
	return TRUE;
}

//-------------------------------------------------------------------------
// Description : seek the synch word
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : bitstream pointer
// Output : SndBool TRUE if found
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bSeekSynch(SND_tdstBitStream* pBitStream)
{
	unsigned short uwSynch = 0;
	unsigned short uwTmp;
	SndBool bFound = FALSE;

	if (SND_fn_bGetNBits(pBitStream,&uwSynch,12)) {
		if (uwSynch == kSynchWord) return TRUE;
		// We must search again...
		while(!bFound) {
			uwSynch <<= 1;
			if (!SND_fn_bGet1Bit(pBitStream,&uwTmp))
				return FALSE; // End of file...

			uwSynch |= uwTmp;
			uwSynch &= 0xfff;
			bFound = (uwSynch == kSynchWord);
		}

		// We have found the synch word...
		return TRUE;

	} else return FALSE;
}

//-------------------------------------------------------------------------
// Description : Fill the buffer
//-------------------------------------------------------------------------
//
//-------------------------------------------------------------------------
// Input  : bitstream pointer
// Output : SndBool TRUE if not EOF
//-------------------------------------------------------------------------
// Creation date : 18/03/97              Author : Guillaume Michel
//-------------------------------------------------------------------------
// Modifications :
// Modification Date :              Author :
//-------------------------------------------------------------------------

SndBool SND_fn_bFillBuffer(SND_tdstBitStream* pBitStream)
{
	unsigned char *pucEnd;
	unsigned long ulSizeAvailable;

	snd_assert(pBitStream);
	
	if (pBitStream->pFile!=OPENFILESND_FAILED) {
		// FILE version 
		// MMIO TO SEE
		pBitStream->ulLastRead = SND_fn_dwReadFileSnd(pBitStream->pFile,kSizeOfBuffer,pBitStream->pucBuffer);
		pBitStream->pucCurrentPointer = pBitStream->pucBuffer; 
		pBitStream->ucCurrentByte = *pBitStream->pucCurrentPointer;
		pBitStream->ulBytePos = 0;
		pBitStream->ucBitPos = 0;
		return (pBitStream->ulLastRead != 0);
	} else {
		// MEMORY version
		pucEnd = pBitStream->pucInputBuffer + pBitStream->ulSize;
		ulSizeAvailable = pucEnd - pBitStream->pucCurrentPointer;
		if (ulSizeAvailable > kSizeOfBuffer) ulSizeAvailable = kSizeOfBuffer;

		pBitStream->ulLastRead = ulSizeAvailable;
		pBitStream->ucCurrentByte = *pBitStream->pucCurrentPointer;
		pBitStream->ulBytePos = 0;
		pBitStream->ucBitPos = 0;
		return (pBitStream->ulLastRead != 0);
	}
}
