// // 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(this); AddRef(); return S_OK; } else if (riid == IID_IDirectMusicObject) { *ppvObj = static_cast(this); AddRef(); return S_OK; } else if (riid == IID_IDirectMusicObjectP) { *ppvObj = static_cast(this); } else if (riid == IID_IPersistStream) { *ppvObj = static_cast(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 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; }