Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

981 lines
30 KiB

//
// Container.cpp: Implementation of CContainer
//
// Copyright (c) 1999-2001 Microsoft Corporation
//
#include "dmusicc.h"
#include "dmusici.h"
#include "dmusicf.h"
#include "validate.h"
#include "container.h"
#include "debug.h"
#include "riff.h"
#include "dmscriptautguids.h"
#include "smartref.h"
#include "miscutil.h"
#ifdef UNDER_CE
#include "dragon.h"
#endif
extern long g_cComponent;
CContainerItem::CContainerItem(bool fEmbedded)
{
m_Desc.dwSize = sizeof(m_Desc);
m_Desc.dwValidData = 0;
m_dwFlags = 0;
m_pObject = NULL;
m_fEmbedded = fEmbedded;
m_pwszAlias = NULL;
}
CContainerItem::~CContainerItem()
{
if (m_pObject)
{
m_pObject->Release();
}
if (m_Desc.dwValidData & DMUS_OBJ_STREAM)
{
SafeRelease(m_Desc.pStream);
}
delete m_pwszAlias;
}
CContainer::CContainer()
{
m_cRef = 1;
m_dwFlags = 0;
m_dwPartialLoad = 0;
m_dwValidData = 0;
m_pStream = NULL;
m_fZombie = false;
}
CContainer::~CContainer()
{
Clear();
if (m_pStream)
{
m_pStream->Release();
}
}
void CContainer::Clear()
{
IDirectMusicLoader *pLoader = NULL;
IDirectMusicGetLoader *pGetLoader = NULL;
if (m_pStream)
{
m_pStream->QueryInterface(IID_IDirectMusicGetLoader,(void **) &pGetLoader);
if (pGetLoader)
{
pGetLoader->GetLoader(&pLoader);
pGetLoader->Release();
}
}
CContainerItem *pItem = m_ItemList.GetHead();
CContainerItem *pNext;
for (;pItem;pItem = pNext)
{
pNext = pItem->GetNext();
if (pItem->m_pObject)
{
if (pLoader && !(pItem->m_dwFlags & DMUS_CONTAINED_OBJF_KEEP))
{
pLoader->ReleaseObject(pItem->m_pObject);
}
pItem->m_pObject->Release();
pItem->m_pObject = NULL;
}
delete pItem;
}
if (pLoader)
{
pLoader->Release();
}
}
STDMETHODIMP_(void) CContainer::Zombie()
{
Clear();
if (m_pStream)
{
m_pStream->Release();
m_pStream = NULL;
}
m_fZombie = true;
}
STDMETHODIMP_(ULONG) CContainer::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) CContainer::Release()
{
if (!InterlockedDecrement(&m_cRef))
{
delete this;
return 0;
}
return m_cRef;
}
STDMETHODIMP CContainer::QueryInterface( const IID &riid, void **ppvObj )
{
if (riid == IID_IUnknown || riid == IID_IDirectMusicContainer) {
*ppvObj = static_cast<IDirectMusicContainer*>(this);
AddRef();
return S_OK;
}
else if (riid == IID_IDirectMusicObject)
{
*ppvObj = static_cast<IDirectMusicObject*>(this);
AddRef();
return S_OK;
}
else if (riid == IID_IDirectMusicObjectP)
{
*ppvObj = static_cast<IDirectMusicObjectP*>(this);
}
else if (riid == IID_IPersistStream)
{
*ppvObj = static_cast<IPersistStream*>(this);
AddRef();
return S_OK;
}
*ppvObj = NULL;
return E_NOINTERFACE;
}
HRESULT CContainer::EnumObject(REFGUID rguidClass,
DWORD dwIndex,
LPDMUS_OBJECTDESC pDesc,
WCHAR *pwszAlias)
{
V_INAME(CContainer::EnumObject);
V_PTR_WRITE_OPT(pDesc, LPDMUS_OBJECTDESC);
V_BUFPTR_WRITE_OPT(pwszAlias, MAX_PATH);
V_REFGUID(rguidClass);
if (m_fZombie)
{
Trace(1, "Error: Call of IDirectMusicContainer::EnumObject after the container has been garbage collected. "
"It is invalid to continue using a container after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
"and then calling CollectGarbage or Release on the loader.");
return DMUS_S_GARBAGE_COLLECTED;
}
CContainerItem *pItem = m_ItemList.GetHead();
DWORD dwCounter = 0;
HRESULT hr = S_FALSE;
for (;pItem;pItem = pItem->GetNext())
{
if ((rguidClass == GUID_DirectMusicAllTypes) ||
(rguidClass == pItem->m_Desc.guidClass))
{
if (dwCounter == dwIndex)
{
hr = S_OK;
if (pDesc)
{
DWORD dwCopySize = min(pDesc->dwSize,pItem->m_Desc.dwSize);
memcpy(pDesc,&pItem->m_Desc,dwCopySize);
if (pDesc->dwValidData & DMUS_OBJ_STREAM && pDesc->pStream)
pDesc->pStream->AddRef();
}
if (pwszAlias)
{
hr = wcsTruncatedCopy(pwszAlias, pItem->m_pwszAlias ? pItem->m_pwszAlias : L"", MAX_PATH);
}
break;
}
dwCounter++;
}
}
return hr;
}
HRESULT CContainer::Load(IStream* pStream, IDirectMusicLoader *pLoader)
{
IRIFFStream *pRiffStream = NULL;
HRESULT hr = AllocRIFFStream(pStream, &pRiffStream);
if(FAILED(hr))
{
return hr;
}
MMCKINFO ckMain;
ckMain.fccType = DMUS_FOURCC_CONTAINER_FORM;
hr = pRiffStream->Descend(&ckMain, NULL, MMIO_FINDRIFF);
if(FAILED(hr))
{
pRiffStream->Release();
return hr;
}
m_dwPartialLoad = FALSE;
MMCKINFO ckNext;
MMCKINFO ckUNFO;
DWORD cbRead;
DWORD cbSize;
DMUS_IO_CONTAINER_HEADER ioHeader;
ckNext.ckid = 0;
ckNext.fccType = 0;
hr = pRiffStream->Descend(&ckNext, &ckMain, 0);
while(SUCCEEDED(hr))
{
switch(ckNext.ckid)
{
case DMUS_FOURCC_CONTAINER_CHUNK :
cbSize = min( sizeof(DMUS_IO_CONTAINER_HEADER), ckNext.cksize );
hr = pStream->Read(&ioHeader, cbSize, &cbRead);
if(SUCCEEDED(hr))
{
m_dwFlags = ioHeader.dwFlags;
}
break;
case DMUS_FOURCC_GUID_CHUNK:
cbSize = sizeof(GUID);
if( ckNext.cksize == cbSize )
{
hr = pStream->Read( &m_guidObject, cbSize, &cbRead );
if( SUCCEEDED(hr) && (cbRead == cbSize) )
{
m_dwValidData |= DMUS_OBJ_OBJECT;
}
}
break;
case DMUS_FOURCC_VERSION_CHUNK:
hr = pStream->Read(&m_vVersion, sizeof(DMUS_IO_VERSION), &cbRead);
if(SUCCEEDED(hr))
{
m_dwValidData |= DMUS_OBJ_VERSION;
}
break;
case DMUS_FOURCC_CATEGORY_CHUNK:
{
cbSize = min(sizeof(m_wszCategory), ckNext.cksize);
hr = pStream->Read(m_wszCategory, cbSize, &cbRead);
if(SUCCEEDED(hr))
{
m_dwValidData |= DMUS_OBJ_CATEGORY;
}
}
break;
case DMUS_FOURCC_DATE_CHUNK:
hr = pStream->Read(&(m_ftDate), sizeof(FILETIME), &cbRead);
if(SUCCEEDED(hr))
{
m_dwValidData |= DMUS_OBJ_DATE;
}
break;
case FOURCC_LIST:
case FOURCC_RIFF:
switch(ckNext.fccType)
{
case DMUS_FOURCC_UNFO_LIST:
while( pRiffStream->Descend( &ckUNFO, &ckNext, 0 ) == S_OK )
{
switch( ckUNFO.ckid )
{
case DMUS_FOURCC_UNAM_CHUNK:
{
cbSize = min(sizeof(m_wszName), ckUNFO.cksize);
hr = pStream->Read(&m_wszName, cbSize, &cbRead);
if(SUCCEEDED(hr))
{
m_dwValidData |= DMUS_OBJ_NAME;
}
break;
}
default:
break;
}
pRiffStream->Ascend( &ckUNFO, 0 );
}
break;
case DMUS_FOURCC_CONTAINED_OBJECTS_LIST :
hr = LoadObjects(pStream, pRiffStream, ckNext, pLoader);
break;
}
break;
}
if(SUCCEEDED(hr))
{
hr = pRiffStream->Ascend(&ckNext, 0);
}
if(SUCCEEDED(hr))
{
ckNext.ckid = 0;
ckNext.fccType = 0;
hr = pRiffStream->Descend(&ckNext, &ckMain, 0);
}
}
// DMUS_E_DESCEND_CHUNK_FAIL is returned when the end of the file
// was reached before the desired chunk was found. In the usage
// above we will also get this error if we have finished parsing the file.
// So we need to set hr to S_OK since we are done
hr = (hr == DMUS_E_DESCEND_CHUNK_FAIL) ? S_OK : hr;
if (SUCCEEDED(hr))
{
// Ascend completely out of the container.
hr = pRiffStream->Ascend(&ckMain, 0);
if (!(m_dwFlags & DMUS_CONTAINER_NOLOADS))
{
for (CContainerItem *pItem = m_ItemList.GetHead();pItem;pItem = pItem->GetNext())
{
if (FAILED(pLoader->GetObject(&pItem->m_Desc,
IID_IDirectMusicObject,
(void **)&pItem->m_pObject)))
{
hr = DMUS_S_PARTIALLOAD;
}
}
}
if (m_pStream)
{
m_pStream->Release();
}
m_pStream = pStream;
m_pStream->AddRef();
}
if(pRiffStream)
{
pRiffStream->Release();
}
return hr;
}
HRESULT CContainer::LoadObjects(IStream *pStream,
IRIFFStream *pRiffStream,
MMCKINFO ckParent,
IDirectMusicLoader *pLoader)
{
MMCKINFO ckNext;
ckNext.ckid = 0;
ckNext.fccType = 0;
HRESULT hr = pRiffStream->Descend(&ckNext, &ckParent, 0);
while(SUCCEEDED(hr))
{
switch(ckNext.ckid)
{
case FOURCC_RIFF:
case FOURCC_LIST:
switch(ckNext.fccType)
{
case DMUS_FOURCC_CONTAINED_OBJECT_LIST :
hr = LoadObject(pStream, pRiffStream, ckNext, pLoader);
break;
}
break;
}
if(SUCCEEDED(hr))
{
hr = pRiffStream->Ascend(&ckNext, 0);
}
if(SUCCEEDED(hr))
{
ckNext.ckid = 0;
ckNext.fccType = 0;
hr = pRiffStream->Descend(&ckNext, &ckParent, 0);
}
}
// DMUS_E_DESCEND_CHUNK_FAIL is returned when the end of the file
// was reached before the desired chunk was found. In the usage
// above we will also get this error if we have finished parsing the file.
// So we need to set hr to S_OK since we are done
hr = (hr == DMUS_E_DESCEND_CHUNK_FAIL) ? S_OK : hr;
return hr;
}
HRESULT CContainer::LoadObject(IStream* pStream,
IRIFFStream *pRiffStream,
MMCKINFO ckParent,
IDirectMusicLoader *pLoader)
{
MMCKINFO ckNext, ckLast;
ckNext.ckid = 0;
ckNext.fccType = 0;
DWORD cbRead;
DWORD cbSize;
DMUS_IO_CONTAINED_OBJECT_HEADER ioHeader;
HRESULT hr = pRiffStream->Descend(&ckNext, &ckParent, 0);
SmartRef::Buffer<WCHAR> wbufAlias;
if(SUCCEEDED(hr))
{
if(ckNext.ckid == DMUS_FOURCC_CONTAINED_ALIAS_CHUNK)
{
if(ckNext.cksize % 2 != 0)
{
assert(false); // should be WCHARs -- two byte pairs
}
else
{
wbufAlias.Alloc(ckNext.cksize / 2);
if (!wbufAlias)
return E_OUTOFMEMORY;
hr = pStream->Read(wbufAlias, ckNext.cksize, &cbRead);
if (FAILED(hr))
return hr;
}
pRiffStream->Ascend(&ckNext, 0);
hr = pRiffStream->Descend(&ckNext, &ckParent, 0);
}
}
if(SUCCEEDED(hr))
{
if(ckNext.ckid != DMUS_FOURCC_CONTAINED_OBJECT_CHUNK)
{
Trace(1,"Invalid object in Container - cobh is not first chunk.\n");
return DMUS_E_INVALID_CONTAINER_OBJECT;
}
cbSize = sizeof(DMUS_IO_CONTAINED_OBJECT_HEADER);
hr = pStream->Read(&ioHeader, cbSize, &cbRead);
if(FAILED(hr))
{
return hr;
}
if(ioHeader.ckid == 0 && ioHeader.fccType == NULL)
{
Trace(1,"Invalid object header in Container.\n");
return DMUS_E_INVALID_CONTAINER_OBJECT;
}
// Move to start of next chunk.
pRiffStream->Ascend(&ckNext, 0);
ckLast = ckNext; // Memorize this position.
hr = pRiffStream->Descend(&ckNext, &ckParent, 0);
while(SUCCEEDED(hr))
{
if((((ckNext.ckid == FOURCC_LIST) || (ckNext.ckid == FOURCC_RIFF))
&& ckNext.fccType == ioHeader.fccType) ||
(ckNext.ckid == ioHeader.ckid))
{
// Okay, this is the chunk we are looking for.
// Seek back to start of chunk.
bool fEmbedded = !(ckNext.ckid == FOURCC_LIST && ckNext.fccType == DMUS_FOURCC_REF_LIST);
CContainerItem *pItem = new CContainerItem(fEmbedded);
if (!pItem)
hr = E_OUTOFMEMORY;
else
{
if (fEmbedded)
{
// This is an embedded object. Ascend to the position where from which it will be loaded.
pRiffStream->Ascend(&ckLast, 0);
pItem->m_Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_STREAM;
pItem->m_Desc.guidClass = ioHeader.guidClassID;
pItem->m_Desc.pStream = pStream;
pStream->AddRef();
}
else
{
// This is a reference chunk. Read the object descriptor.
hr = this->ReadReference(pStream, pRiffStream, ckNext, &pItem->m_Desc);
}
if (SUCCEEDED(hr))
{
// We will call SetObject on items in the container here. The items are loaded later.
// This ensures that out-of-order references between objects can be retrieved as the objects
// load themselves.
pLoader->SetObject(&pItem->m_Desc);
if (pItem->m_Desc.dwValidData & DMUS_OBJ_STREAM)
{
// The loader has the stream now so we don't need it any more.
pItem->m_Desc.dwValidData &= ~DMUS_OBJ_STREAM;
SafeRelease(pItem->m_Desc.pStream);
}
pItem->m_pwszAlias = wbufAlias.disown();
m_ItemList.AddTail(pItem);
}
else
delete pItem;
}
}
if(SUCCEEDED(hr))
{
pRiffStream->Ascend(&ckNext, 0);
ckLast = ckNext;
{
ckNext.ckid = 0;
ckNext.fccType = 0;
hr = pRiffStream->Descend(&ckNext, &ckParent, 0);
}
}
}
// DMUS_E_DESCEND_CHUNK_FAIL is returned when the end of the file
// was reached before the desired chunk was found. In the usage
// above we will also get this error if we have finished parsing the file.
// So we need to set hr to S_OK since we are done
hr = (hr == DMUS_E_DESCEND_CHUNK_FAIL) ? S_OK : hr;
}
return hr;
}
HRESULT
CContainer::ReadReference(IStream* pStream,
IRIFFStream *pRiffStream,
MMCKINFO ckParent,
DMUS_OBJECTDESC *pDesc)
{
// I can't believe I'm writing this function! It's copied right out of WaveItem::LoadReference and modified to work here.
// This really aught to be shared code, but the other components all use different stream reader thingies than IRIFFStream
// so that won't work.
if (!pStream || !pRiffStream || !pDesc)
{
assert(false);
return E_INVALIDARG;
}
ZeroAndSize(pDesc);
DWORD cbRead;
MMCKINFO ckNext;
ckNext.ckid = 0;
ckNext.fccType = 0;
DWORD dwSize = 0;
HRESULT hr = S_OK;
while( pRiffStream->Descend( &ckNext, &ckParent, 0 ) == S_OK )
{
switch(ckNext.ckid)
{
case DMUS_FOURCC_REF_CHUNK:
DMUS_IO_REFERENCE ioDMRef;
hr = pStream->Read(&ioDMRef, sizeof(DMUS_IO_REFERENCE), &cbRead);
if(SUCCEEDED(hr))
{
pDesc->guidClass = ioDMRef.guidClassID;
pDesc->dwValidData |= ioDMRef.dwValidData;
pDesc->dwValidData |= DMUS_OBJ_CLASS;
}
break;
case DMUS_FOURCC_GUID_CHUNK:
hr = pStream->Read(&(pDesc->guidObject), sizeof(GUID), &cbRead);
if(SUCCEEDED(hr))
{
pDesc->dwValidData |= DMUS_OBJ_OBJECT;
}
break;
case DMUS_FOURCC_DATE_CHUNK:
hr = pStream->Read(&(pDesc->ftDate), sizeof(FILETIME), &cbRead);
if(SUCCEEDED(hr))
{
pDesc->dwValidData |= DMUS_OBJ_DATE;
}
break;
case DMUS_FOURCC_NAME_CHUNK:
dwSize = min(sizeof(pDesc->wszName), ckNext.cksize);
hr = pStream->Read(pDesc->wszName, dwSize, &cbRead);
if(SUCCEEDED(hr))
{
pDesc->wszName[DMUS_MAX_NAME - 1] = L'\0';
pDesc->dwValidData |= DMUS_OBJ_NAME;
}
break;
case DMUS_FOURCC_FILE_CHUNK:
dwSize = min(sizeof(pDesc->wszFileName), ckNext.cksize);
hr = pStream->Read(pDesc->wszFileName, dwSize, &cbRead);
if(SUCCEEDED(hr))
{
pDesc->wszFileName[DMUS_MAX_FILENAME - 1] = L'\0';
pDesc->dwValidData |= DMUS_OBJ_FILENAME;
}
break;
case DMUS_FOURCC_CATEGORY_CHUNK:
dwSize = min(sizeof(pDesc->wszCategory), ckNext.cksize);
hr = pStream->Read(pDesc->wszCategory, dwSize, &cbRead);
if(SUCCEEDED(hr))
{
pDesc->wszCategory[DMUS_MAX_CATEGORY - 1] = L'\0';
pDesc->dwValidData |= DMUS_OBJ_CATEGORY;
}
break;
case DMUS_FOURCC_VERSION_CHUNK:
DMUS_IO_VERSION ioDMObjVer;
hr = pStream->Read(&ioDMObjVer, sizeof(DMUS_IO_VERSION), &cbRead);
if(SUCCEEDED(hr))
{
pDesc->vVersion.dwVersionMS = ioDMObjVer.dwVersionMS;
pDesc->vVersion.dwVersionLS = ioDMObjVer.dwVersionLS;
pDesc->dwValidData |= DMUS_OBJ_VERSION;
}
break;
default:
break;
}
if(SUCCEEDED(hr) && pRiffStream->Ascend(&ckNext, 0) == S_OK)
{
ckNext.ckid = 0;
ckNext.fccType = 0;
}
else if (SUCCEEDED(hr)) hr = DMUS_E_CANNOTREAD;
}
if (!(pDesc->dwValidData & DMUS_OBJ_NAME) &&
!(pDesc->dwValidData & DMUS_OBJ_FILENAME) &&
!(pDesc->dwValidData & DMUS_OBJ_OBJECT) )
{
Trace(1,"Error: Incomplete object reference in Container - DMRF must specify an object name, filename, or GUID.\n");
hr = DMUS_E_INVALID_CONTAINER_OBJECT;
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
// IPersist
HRESULT CContainer::GetClassID( CLSID* pClassID )
{
if (m_fZombie)
{
Trace(1, "Error: Call of IDirectMusicContainer::GetClassID after the container has been garbage collected. "
"It is invalid to continue using a container after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
"and then calling CollectGarbage or Release on the loader.");
return DMUS_S_GARBAGE_COLLECTED;
}
if (pClassID)
{
*pClassID = CLSID_DirectMusicContainer;
return S_OK;
}
return E_POINTER;
}
/////////////////////////////////////////////////////////////////////////////
// IPersistStream functions
HRESULT CContainer::IsDirty()
{
if (m_fZombie)
{
Trace(1, "Error: Call of IDirectMusicContainer::IsDirty after the container has been garbage collected. "
"It is invalid to continue using a container after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
"and then calling CollectGarbage or Release on the loader.");
return DMUS_S_GARBAGE_COLLECTED;
}
return S_FALSE;
}
HRESULT CContainer::Load( IStream* pStream )
{
V_INAME(IPersistStream::Load);
V_INTERFACE(pStream);
if (m_fZombie)
{
Trace(1, "Error: Call of IDirectMusicContainer::Load after the container has been garbage collected. "
"It is invalid to continue using a container after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
"and then calling CollectGarbage or Release on the loader.");
return DMUS_S_GARBAGE_COLLECTED;
}
IDirectMusicLoader *pLoader = NULL;
IDirectMusicGetLoader *pGetLoader = NULL;
if (pStream)
{
pStream->QueryInterface(IID_IDirectMusicGetLoader,(void **) &pGetLoader);
if (pGetLoader)
{
pGetLoader->GetLoader(&pLoader);
pGetLoader->Release();
}
}
if (pLoader)
{
HRESULT hr = Load(pStream, pLoader);
pLoader->Release();
return hr;
}
Trace(1, "Error: unable to load container from a stream because it doesn't support the IDirectMusicGetLoader interface.\n");
return DMUS_E_UNSUPPORTED_STREAM;
}
HRESULT CContainer::Save( IStream* pIStream, BOOL fClearDirty )
{
return E_NOTIMPL;
}
HRESULT CContainer::GetSizeMax( ULARGE_INTEGER FAR* pcbSize )
{
return E_NOTIMPL;
}
/////////////////////////////////////////////////////////////////////////////
// IDirectMusicObject
STDMETHODIMP CContainer::GetDescriptor(LPDMUS_OBJECTDESC pDesc)
{
// Argument validation
V_INAME(CContainer::GetDescriptor);
V_PTR_WRITE(pDesc, DMUS_OBJECTDESC);
if (m_fZombie)
{
Trace(1, "Error: Call of IDirectMusicContainer::GetDescriptor after the container has been garbage collected. "
"It is invalid to continue using a container after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
"and then calling CollectGarbage or Release on the loader.");
return DMUS_S_GARBAGE_COLLECTED;
}
memset( pDesc, 0, sizeof(DMUS_OBJECTDESC));
pDesc->dwSize = sizeof(DMUS_OBJECTDESC);
pDesc->guidClass = CLSID_DirectMusicContainer;
pDesc->guidObject = m_guidObject;
pDesc->ftDate = m_ftDate;
pDesc->vVersion = m_vVersion;
memcpy( pDesc->wszName, m_wszName, sizeof(m_wszName) );
memcpy( pDesc->wszCategory, m_wszCategory, sizeof(m_wszCategory) );
memcpy( pDesc->wszFileName, m_wszFileName, sizeof(m_wszFileName) );
pDesc->dwValidData = ( m_dwValidData | DMUS_OBJ_CLASS );
return S_OK;
}
STDMETHODIMP CContainer::SetDescriptor(LPDMUS_OBJECTDESC pDesc)
{
// Argument validation
V_INAME(CContainer::SetDescriptor);
V_PTR_READ(pDesc, DMUS_OBJECTDESC);
if (m_fZombie)
{
Trace(1, "Error: Call of IDirectMusicContainer::SetDescriptor after the container has been garbage collected. "
"It is invalid to continue using a container after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
"and then calling CollectGarbage or Release on the loader.");
return DMUS_S_GARBAGE_COLLECTED;
}
HRESULT hr = S_OK;
DWORD dw = 0;
if( pDesc->dwValidData & DMUS_OBJ_OBJECT )
{
m_guidObject = pDesc->guidObject;
dw |= DMUS_OBJ_OBJECT;
}
if( pDesc->dwValidData & DMUS_OBJ_NAME )
{
memcpy( m_wszName, pDesc->wszName, sizeof(WCHAR)*DMUS_MAX_NAME );
dw |= DMUS_OBJ_NAME;
}
if( pDesc->dwValidData & DMUS_OBJ_CATEGORY )
{
memcpy( m_wszCategory, pDesc->wszCategory, sizeof(WCHAR)*DMUS_MAX_CATEGORY );
dw |= DMUS_OBJ_CATEGORY;
}
if( ( pDesc->dwValidData & DMUS_OBJ_FILENAME ) ||
( pDesc->dwValidData & DMUS_OBJ_FULLPATH ) )
{
memcpy( m_wszFileName, pDesc->wszFileName, sizeof(WCHAR)*DMUS_MAX_FILENAME );
dw |= (pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH));
}
if( pDesc->dwValidData & DMUS_OBJ_VERSION )
{
m_vVersion = pDesc->vVersion;
dw |= DMUS_OBJ_VERSION;
}
if( pDesc->dwValidData & DMUS_OBJ_DATE )
{
m_ftDate = pDesc->ftDate;
dw |= DMUS_OBJ_DATE;
}
m_dwValidData |= dw;
if( pDesc->dwValidData & (~dw) )
{
hr = S_FALSE; // there were extra fields we didn't parse;
pDesc->dwValidData = dw;
}
return hr;
}
STDMETHODIMP CContainer::ParseDescriptor(LPSTREAM pStream, LPDMUS_OBJECTDESC pDesc)
{
V_INAME(CContainer::ParseDescriptor);
V_INTERFACE(pStream);
V_STRUCTPTR_WRITE(pDesc, DMUS_OBJECTDESC);
if (m_fZombie)
{
Trace(1, "Error: Call of IDirectMusicContainer::ParseDescriptor after the container has been garbage collected. "
"It is invalid to continue using a container after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
"and then calling CollectGarbage or Release on the loader.");
return DMUS_S_GARBAGE_COLLECTED;
}
IRIFFStream *pRiffStream = NULL;
HRESULT hr = AllocRIFFStream(pStream, &pRiffStream);
if (FAILED(hr))
return hr;
MMCKINFO ckMain;
ckMain.fccType = DMUS_FOURCC_CONTAINER_FORM;
hr = pRiffStream->Descend(&ckMain, NULL, MMIO_FINDRIFF);
if(FAILED(hr))
{
pRiffStream->Release();
return hr;
}
pDesc->dwValidData = DMUS_OBJ_CLASS;
pDesc->guidClass = CLSID_DirectMusicContainer;
MMCKINFO ckNext;
MMCKINFO ckUNFO;
DWORD cbRead;
DWORD cbSize;
ckNext.ckid = 0;
ckNext.fccType = 0;
hr = pRiffStream->Descend(&ckNext, &ckMain, 0);
while(SUCCEEDED(hr))
{
switch(ckNext.ckid)
{
case DMUS_FOURCC_GUID_CHUNK:
cbSize = sizeof(GUID);
if( ckNext.cksize == cbSize )
{
hr = pStream->Read( &pDesc->guidObject, cbSize, &cbRead );
if( SUCCEEDED(hr) && (cbRead == cbSize) )
{
pDesc->dwValidData |= DMUS_OBJ_OBJECT;
}
}
break;
case DMUS_FOURCC_VERSION_CHUNK:
hr = pStream->Read(&pDesc->vVersion, sizeof(DMUS_IO_VERSION), &cbRead);
if(SUCCEEDED(hr))
{
pDesc->dwValidData |= DMUS_OBJ_VERSION;
}
break;
case DMUS_FOURCC_CATEGORY_CHUNK:
{
cbSize = min(sizeof(pDesc->wszCategory), ckNext.cksize);
hr = pStream->Read(pDesc->wszCategory, cbSize, &cbRead);
if(SUCCEEDED(hr))
{
pDesc->dwValidData |= DMUS_OBJ_CATEGORY;
}
}
break;
case DMUS_FOURCC_DATE_CHUNK:
hr = pStream->Read(&(pDesc->ftDate), sizeof(FILETIME), &cbRead);
if(SUCCEEDED(hr))
{
pDesc->dwValidData |= DMUS_OBJ_DATE;
}
break;
case FOURCC_LIST:
switch(ckNext.fccType)
{
case DMUS_FOURCC_UNFO_LIST:
while( pRiffStream->Descend( &ckUNFO, &ckNext, 0 ) == S_OK )
{
switch( ckUNFO.ckid )
{
case DMUS_FOURCC_UNAM_CHUNK:
{
cbSize = min(sizeof(pDesc->wszName), ckUNFO.cksize);
hr = pStream->Read(&pDesc->wszName, cbSize, &cbRead);
if(SUCCEEDED(hr))
{
pDesc->dwValidData |= DMUS_OBJ_NAME;
}
break;
}
default:
break;
}
pRiffStream->Ascend( &ckUNFO, 0 );
}
break;
}
break;
}
if(SUCCEEDED(hr))
{
hr = pRiffStream->Ascend(&ckNext, 0);
}
if(SUCCEEDED(hr))
{
ckNext.ckid = 0;
ckNext.fccType = 0;
hr = pRiffStream->Descend(&ckNext, &ckMain, 0);
}
}
// DMUS_E_DESCEND_CHUNK_FAIL is returned when the end of the file
// was reached before the desired chunk was found. In the usage
// above we will also get this error if we have finished parsing the file.
// So we need to set hr to S_OK since we are done
hr = (hr == DMUS_E_DESCEND_CHUNK_FAIL) ? S_OK : hr;
if(pRiffStream)
{
pRiffStream->Release();
}
return hr;
}