//////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2000 Microsoft Corporation.  All Rights Reserved.
//
//  File:       DplayBufHelp.cpp
//  Content:    Helper functions for DPlay Buffers (Byte arrays)
//
//////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Direct.h"

#define SAFE_FREE(p)       { if(p) { free (p); p=NULL; } }
#define SAFE_DELETE(p)       { if(p) { delete (p); p=NULL; } }

#define INITIAL_BUFFER_SIZE 20

HRESULT WINAPI VB_GrowBuffer(SAFEARRAY **Buffer, DWORD dwGrowSize);
HRESULT WINAPI VB_NewBuffer(SAFEARRAY **Buffer, long *lOffSet);
HRESULT WINAPI VB_AddDataToBuffer(SAFEARRAY **Buffer, void *lData, DWORD lSize, long *lOffSet);
HRESULT WINAPI VB_AddStringToBuffer(SAFEARRAY **Buffer, BSTR StringData, long *lOffSet);
HRESULT WINAPI VB_GetDataFromBuffer(SAFEARRAY **Buffer, void *lData, DWORD lSize, long *lOffSet);
HRESULT WINAPI VB_GetStringFromBuffer(SAFEARRAY **Buffer, long *lOffSet, BSTR *sData);

// Functions for writing a buffer
HRESULT WINAPI VB_AddStringToBuffer(SAFEARRAY **Buffer, BSTR StringData, long *lOffSet)
{
	HRESULT hr;
	// For strings we will first write out a DWORD 
	// containging the length of the string.  Then we
	// will write the actual data to the string.

	DWORD dwStrLen= (((DWORD*)StringData)[-1]);
	DWORD dwDataSize = sizeof(DWORD) + dwStrLen;
	
	if (!StringData)
		return E_INVALIDARG;

	if (!((SAFEARRAY*)*Buffer))
	{
		// We need to create this buffer, it doesn't exist
		SAFEARRAY					*lpData = NULL;
		SAFEARRAYBOUND				rgsabound[1];

		// Let's create our SafeArray
		rgsabound[0].lLbound = 0; // A single dimension array that is zero based
		rgsabound[0].cElements = dwDataSize; //Set the initial size
		// Create this data
		lpData = SafeArrayCreate(VT_UI1, 1, rgsabound);

		if (!lpData)
			return E_OUTOFMEMORY;

		(SAFEARRAY*)*Buffer = lpData;
	}

	if (!((SAFEARRAY*)*Buffer)->pvData)
		return E_INVALIDARG;

	// Do we have enough memory for this string right now?
	if (*lOffSet + dwDataSize > ((SAFEARRAY*)*Buffer)->rgsabound[0].cElements)
		if (FAILED( hr = VB_GrowBuffer(Buffer, dwDataSize) ) )
			return hr;

	// Ok, now we' ve got our memory, copy it over
	// First the length
	BYTE  *lPtr = (BYTE*)((SAFEARRAY*)*Buffer)->pvData;
	__try {

		memcpy(lPtr + *lOffSet, &dwStrLen, sizeof(DWORD));
		*lOffSet += sizeof(DWORD);
		// Now the actual string
		memcpy(lPtr + *lOffSet, StringData, dwStrLen);
		*lOffSet += dwStrLen;
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		return E_INVALIDARG;
	}

	return S_OK;

}

HRESULT WINAPI VB_AddDataToBuffer(SAFEARRAY **Buffer, void *lData, DWORD lSize, long *lOffSet)
{
	HRESULT hr;
	
	if (!lData)
		return E_INVALIDARG;

	if (!lSize)
		return E_INVALIDARG;

	if (!((SAFEARRAY*)*Buffer))
	{
		// We need to create this buffer, it doesn't exist
		SAFEARRAY					*lpData = NULL;
		SAFEARRAYBOUND				rgsabound[1];

		// Let's create our SafeArray
		rgsabound[0].lLbound = 0; // A single dimension array that is zero based
		rgsabound[0].cElements = lSize; //Set the initial size
		// Create this data
		lpData = SafeArrayCreate(VT_UI1, 1, rgsabound);

		if (!lpData)
			return E_OUTOFMEMORY;

		(SAFEARRAY*)*Buffer = lpData;
	}

	if (!((SAFEARRAY*)*Buffer)->pvData)
		return E_INVALIDARG;

	// Do we have enough memory for this string right now?
	if (*lOffSet + lSize > ((SAFEARRAY*)*Buffer)->rgsabound[0].cElements)
		if (FAILED( hr = VB_GrowBuffer(Buffer, lSize) ) )
			return hr;

	BYTE  *lPtr = (BYTE*)((SAFEARRAY*)*Buffer)->pvData;

	__try {
		memcpy(lPtr + *lOffSet, lData, lSize);
		*lOffSet += lSize;
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		return E_INVALIDARG;
	}
	return S_OK;
}

HRESULT WINAPI VB_NewBuffer(SAFEARRAY **Buffer, long *lOffSet)
{
	// Set up with a 20 byte msg at first
	SAFEARRAY					*lpData = NULL;
	SAFEARRAYBOUND				rgsabound[1];

	// Let's create our SafeArray
	rgsabound[0].lLbound = 0; // A single dimension array that is zero based
	rgsabound[0].cElements = INITIAL_BUFFER_SIZE; //Set the initial size
	// Create this data
	lpData = SafeArrayCreate(VT_UI1, 1, rgsabound);

	if (!lpData)
		return E_OUTOFMEMORY;

	(SAFEARRAY*)*Buffer = lpData;

	*lOffSet = 0;
	return S_OK;
}

HRESULT WINAPI VB_GrowBuffer(SAFEARRAY **Buffer, DWORD dwGrowSize)
{
	SAFEARRAY					*lpData = NULL;
	SAFEARRAYBOUND				rgsabound[1];
	DWORD						dwCurSize = 0;

	if (!dwGrowSize)
		return E_INVALIDARG;

	if (!((SAFEARRAY*)*Buffer))
		return E_INVALIDARG;

	if (!((SAFEARRAY*)*Buffer)->pvData)
		return E_INVALIDARG;

	dwCurSize = ((SAFEARRAY*)*Buffer)->rgsabound[0].cElements;

	// Let's create a new SafeArray
	rgsabound[0].lLbound = 0; // A single dimension array that is zero based
	rgsabound[0].cElements = dwCurSize + dwGrowSize; //Set the size
	// Create this data
	lpData = SafeArrayCreate(VT_UI1, 1, rgsabound);

	if (!lpData)
		return E_OUTOFMEMORY;

	__try {
		memcpy(lpData->pvData, ((SAFEARRAY*)*Buffer)->pvData, dwCurSize);
		SafeArrayDestroy((SAFEARRAY*)*Buffer);

		(SAFEARRAY*)*Buffer = lpData;
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		return E_FAIL;
	}

	return S_OK;
}

HRESULT WINAPI VB_GetDataFromBuffer(SAFEARRAY **Buffer, void *lData, DWORD lSize, long *lOffSet)
{
	// Simply copy the memory from the offset to the new data

	if (!lData)
		return E_INVALIDARG;

	if (!lSize)
		return E_INVALIDARG;

	if (!(SAFEARRAY*)*Buffer)
		return E_INVALIDARG;

	if (!((SAFEARRAY*)*Buffer)->pvData)
		return E_INVALIDARG;

	if (*lOffSet + lSize > ((SAFEARRAY*)*Buffer)->rgsabound[0].cElements)
		return E_INVALIDARG;

	BYTE  *lPtr = (BYTE*)((SAFEARRAY*)*Buffer)->pvData;

	__try {
		memcpy(lData, lPtr + *lOffSet, lSize);
		*lOffSet += lSize;
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		return E_INVALIDARG;
	}

	return S_OK;
}

HRESULT WINAPI VB_GetStringFromBuffer(SAFEARRAY **Buffer, long *lOffSet, BSTR *sData)
{
	DWORD		dwStrLen = 0;
	WCHAR		*sNewString = NULL;

	// Simply copy the memory from the offset to the new data
	if (!(SAFEARRAY*)*Buffer)
		return E_INVALIDARG;

	if (!((SAFEARRAY*)*Buffer)->pvData)
		return E_INVALIDARG;

	if (*lOffSet + sizeof(DWORD) > ((SAFEARRAY*)*Buffer)->rgsabound[0].cElements)
		return E_INVALIDARG;

	BYTE  *lPtr = (BYTE*)((SAFEARRAY*)*Buffer)->pvData;

	__try {
		// First read the size of the string
		dwStrLen = *(DWORD*)(lPtr + *lOffSet);
		*lOffSet += sizeof(DWORD);

		if (*lOffSet + dwStrLen  > ((SAFEARRAY*)*Buffer)->rgsabound[0].cElements)
			return E_INVALIDARG;

		sNewString = (WCHAR*)new BYTE[dwStrLen+2];
		if (!sNewString)
			return E_OUTOFMEMORY;

		ZeroMemory(sNewString, dwStrLen+2);
		memcpy(sNewString, lPtr + *lOffSet, dwStrLen);
		*sData = SysAllocString(sNewString);

		*lOffSet += (dwStrLen);
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		return E_INVALIDARG;
	}

	return S_OK;
}