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

  1. //
  2. // Container.cpp: Implementation of CContainer
  3. //
  4. // Copyright (c) 1999-2001 Microsoft Corporation
  5. //
  6. #include "dmusicc.h"
  7. #include "dmusici.h"
  8. #include "dmusicf.h"
  9. #include "validate.h"
  10. #include "container.h"
  11. #include "debug.h"
  12. #include "riff.h"
  13. #include "dmscriptautguids.h"
  14. #include "smartref.h"
  15. #include "miscutil.h"
  16. #ifdef UNDER_CE
  17. #include "dragon.h"
  18. #endif
  19. extern long g_cComponent;
  20. CContainerItem::CContainerItem(bool fEmbedded)
  21. {
  22. m_Desc.dwSize = sizeof(m_Desc);
  23. m_Desc.dwValidData = 0;
  24. m_dwFlags = 0;
  25. m_pObject = NULL;
  26. m_fEmbedded = fEmbedded;
  27. m_pwszAlias = NULL;
  28. }
  29. CContainerItem::~CContainerItem()
  30. {
  31. if (m_pObject)
  32. {
  33. m_pObject->Release();
  34. }
  35. if (m_Desc.dwValidData & DMUS_OBJ_STREAM)
  36. {
  37. SafeRelease(m_Desc.pStream);
  38. }
  39. delete m_pwszAlias;
  40. }
  41. CContainer::CContainer()
  42. {
  43. m_cRef = 1;
  44. m_dwFlags = 0;
  45. m_dwPartialLoad = 0;
  46. m_dwValidData = 0;
  47. m_pStream = NULL;
  48. m_fZombie = false;
  49. }
  50. CContainer::~CContainer()
  51. {
  52. Clear();
  53. if (m_pStream)
  54. {
  55. m_pStream->Release();
  56. }
  57. }
  58. void CContainer::Clear()
  59. {
  60. IDirectMusicLoader *pLoader = NULL;
  61. IDirectMusicGetLoader *pGetLoader = NULL;
  62. if (m_pStream)
  63. {
  64. m_pStream->QueryInterface(IID_IDirectMusicGetLoader,(void **) &pGetLoader);
  65. if (pGetLoader)
  66. {
  67. pGetLoader->GetLoader(&pLoader);
  68. pGetLoader->Release();
  69. }
  70. }
  71. CContainerItem *pItem = m_ItemList.GetHead();
  72. CContainerItem *pNext;
  73. for (;pItem;pItem = pNext)
  74. {
  75. pNext = pItem->GetNext();
  76. if (pItem->m_pObject)
  77. {
  78. if (pLoader && !(pItem->m_dwFlags & DMUS_CONTAINED_OBJF_KEEP))
  79. {
  80. pLoader->ReleaseObject(pItem->m_pObject);
  81. }
  82. pItem->m_pObject->Release();
  83. pItem->m_pObject = NULL;
  84. }
  85. delete pItem;
  86. }
  87. if (pLoader)
  88. {
  89. pLoader->Release();
  90. }
  91. }
  92. STDMETHODIMP_(void) CContainer::Zombie()
  93. {
  94. Clear();
  95. if (m_pStream)
  96. {
  97. m_pStream->Release();
  98. m_pStream = NULL;
  99. }
  100. m_fZombie = true;
  101. }
  102. STDMETHODIMP_(ULONG) CContainer::AddRef()
  103. {
  104. return InterlockedIncrement(&m_cRef);
  105. }
  106. STDMETHODIMP_(ULONG) CContainer::Release()
  107. {
  108. if (!InterlockedDecrement(&m_cRef))
  109. {
  110. delete this;
  111. return 0;
  112. }
  113. return m_cRef;
  114. }
  115. STDMETHODIMP CContainer::QueryInterface( const IID &riid, void **ppvObj )
  116. {
  117. if (riid == IID_IUnknown || riid == IID_IDirectMusicContainer) {
  118. *ppvObj = static_cast<IDirectMusicContainer*>(this);
  119. AddRef();
  120. return S_OK;
  121. }
  122. else if (riid == IID_IDirectMusicObject)
  123. {
  124. *ppvObj = static_cast<IDirectMusicObject*>(this);
  125. AddRef();
  126. return S_OK;
  127. }
  128. else if (riid == IID_IDirectMusicObjectP)
  129. {
  130. *ppvObj = static_cast<IDirectMusicObjectP*>(this);
  131. }
  132. else if (riid == IID_IPersistStream)
  133. {
  134. *ppvObj = static_cast<IPersistStream*>(this);
  135. AddRef();
  136. return S_OK;
  137. }
  138. *ppvObj = NULL;
  139. return E_NOINTERFACE;
  140. }
  141. HRESULT CContainer::EnumObject(REFGUID rguidClass,
  142. DWORD dwIndex,
  143. LPDMUS_OBJECTDESC pDesc,
  144. WCHAR *pwszAlias)
  145. {
  146. V_INAME(CContainer::EnumObject);
  147. V_PTR_WRITE_OPT(pDesc, LPDMUS_OBJECTDESC);
  148. V_BUFPTR_WRITE_OPT(pwszAlias, MAX_PATH);
  149. V_REFGUID(rguidClass);
  150. if (m_fZombie)
  151. {
  152. Trace(1, "Error: Call of IDirectMusicContainer::EnumObject after the container has been garbage collected. "
  153. "It is invalid to continue using a container after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  154. "and then calling CollectGarbage or Release on the loader.");
  155. return DMUS_S_GARBAGE_COLLECTED;
  156. }
  157. CContainerItem *pItem = m_ItemList.GetHead();
  158. DWORD dwCounter = 0;
  159. HRESULT hr = S_FALSE;
  160. for (;pItem;pItem = pItem->GetNext())
  161. {
  162. if ((rguidClass == GUID_DirectMusicAllTypes) ||
  163. (rguidClass == pItem->m_Desc.guidClass))
  164. {
  165. if (dwCounter == dwIndex)
  166. {
  167. hr = S_OK;
  168. if (pDesc)
  169. {
  170. DWORD dwCopySize = min(pDesc->dwSize,pItem->m_Desc.dwSize);
  171. memcpy(pDesc,&pItem->m_Desc,dwCopySize);
  172. if (pDesc->dwValidData & DMUS_OBJ_STREAM && pDesc->pStream)
  173. pDesc->pStream->AddRef();
  174. }
  175. if (pwszAlias)
  176. {
  177. hr = wcsTruncatedCopy(pwszAlias, pItem->m_pwszAlias ? pItem->m_pwszAlias : L"", MAX_PATH);
  178. }
  179. break;
  180. }
  181. dwCounter++;
  182. }
  183. }
  184. return hr;
  185. }
  186. HRESULT CContainer::Load(IStream* pStream, IDirectMusicLoader *pLoader)
  187. {
  188. IRIFFStream *pRiffStream = NULL;
  189. HRESULT hr = AllocRIFFStream(pStream, &pRiffStream);
  190. if(FAILED(hr))
  191. {
  192. return hr;
  193. }
  194. MMCKINFO ckMain;
  195. ckMain.fccType = DMUS_FOURCC_CONTAINER_FORM;
  196. hr = pRiffStream->Descend(&ckMain, NULL, MMIO_FINDRIFF);
  197. if(FAILED(hr))
  198. {
  199. pRiffStream->Release();
  200. return hr;
  201. }
  202. m_dwPartialLoad = FALSE;
  203. MMCKINFO ckNext;
  204. MMCKINFO ckUNFO;
  205. DWORD cbRead;
  206. DWORD cbSize;
  207. DMUS_IO_CONTAINER_HEADER ioHeader;
  208. ckNext.ckid = 0;
  209. ckNext.fccType = 0;
  210. hr = pRiffStream->Descend(&ckNext, &ckMain, 0);
  211. while(SUCCEEDED(hr))
  212. {
  213. switch(ckNext.ckid)
  214. {
  215. case DMUS_FOURCC_CONTAINER_CHUNK :
  216. cbSize = min( sizeof(DMUS_IO_CONTAINER_HEADER), ckNext.cksize );
  217. hr = pStream->Read(&ioHeader, cbSize, &cbRead);
  218. if(SUCCEEDED(hr))
  219. {
  220. m_dwFlags = ioHeader.dwFlags;
  221. }
  222. break;
  223. case DMUS_FOURCC_GUID_CHUNK:
  224. cbSize = sizeof(GUID);
  225. if( ckNext.cksize == cbSize )
  226. {
  227. hr = pStream->Read( &m_guidObject, cbSize, &cbRead );
  228. if( SUCCEEDED(hr) && (cbRead == cbSize) )
  229. {
  230. m_dwValidData |= DMUS_OBJ_OBJECT;
  231. }
  232. }
  233. break;
  234. case DMUS_FOURCC_VERSION_CHUNK:
  235. hr = pStream->Read(&m_vVersion, sizeof(DMUS_IO_VERSION), &cbRead);
  236. if(SUCCEEDED(hr))
  237. {
  238. m_dwValidData |= DMUS_OBJ_VERSION;
  239. }
  240. break;
  241. case DMUS_FOURCC_CATEGORY_CHUNK:
  242. {
  243. cbSize = min(sizeof(m_wszCategory), ckNext.cksize);
  244. hr = pStream->Read(m_wszCategory, cbSize, &cbRead);
  245. if(SUCCEEDED(hr))
  246. {
  247. m_dwValidData |= DMUS_OBJ_CATEGORY;
  248. }
  249. }
  250. break;
  251. case DMUS_FOURCC_DATE_CHUNK:
  252. hr = pStream->Read(&(m_ftDate), sizeof(FILETIME), &cbRead);
  253. if(SUCCEEDED(hr))
  254. {
  255. m_dwValidData |= DMUS_OBJ_DATE;
  256. }
  257. break;
  258. case FOURCC_LIST:
  259. case FOURCC_RIFF:
  260. switch(ckNext.fccType)
  261. {
  262. case DMUS_FOURCC_UNFO_LIST:
  263. while( pRiffStream->Descend( &ckUNFO, &ckNext, 0 ) == S_OK )
  264. {
  265. switch( ckUNFO.ckid )
  266. {
  267. case DMUS_FOURCC_UNAM_CHUNK:
  268. {
  269. cbSize = min(sizeof(m_wszName), ckUNFO.cksize);
  270. hr = pStream->Read(&m_wszName, cbSize, &cbRead);
  271. if(SUCCEEDED(hr))
  272. {
  273. m_dwValidData |= DMUS_OBJ_NAME;
  274. }
  275. break;
  276. }
  277. default:
  278. break;
  279. }
  280. pRiffStream->Ascend( &ckUNFO, 0 );
  281. }
  282. break;
  283. case DMUS_FOURCC_CONTAINED_OBJECTS_LIST :
  284. hr = LoadObjects(pStream, pRiffStream, ckNext, pLoader);
  285. break;
  286. }
  287. break;
  288. }
  289. if(SUCCEEDED(hr))
  290. {
  291. hr = pRiffStream->Ascend(&ckNext, 0);
  292. }
  293. if(SUCCEEDED(hr))
  294. {
  295. ckNext.ckid = 0;
  296. ckNext.fccType = 0;
  297. hr = pRiffStream->Descend(&ckNext, &ckMain, 0);
  298. }
  299. }
  300. // DMUS_E_DESCEND_CHUNK_FAIL is returned when the end of the file
  301. // was reached before the desired chunk was found. In the usage
  302. // above we will also get this error if we have finished parsing the file.
  303. // So we need to set hr to S_OK since we are done
  304. hr = (hr == DMUS_E_DESCEND_CHUNK_FAIL) ? S_OK : hr;
  305. if (SUCCEEDED(hr))
  306. {
  307. // Ascend completely out of the container.
  308. hr = pRiffStream->Ascend(&ckMain, 0);
  309. if (!(m_dwFlags & DMUS_CONTAINER_NOLOADS))
  310. {
  311. for (CContainerItem *pItem = m_ItemList.GetHead();pItem;pItem = pItem->GetNext())
  312. {
  313. if (FAILED(pLoader->GetObject(&pItem->m_Desc,
  314. IID_IDirectMusicObject,
  315. (void **)&pItem->m_pObject)))
  316. {
  317. hr = DMUS_S_PARTIALLOAD;
  318. }
  319. }
  320. }
  321. if (m_pStream)
  322. {
  323. m_pStream->Release();
  324. }
  325. m_pStream = pStream;
  326. m_pStream->AddRef();
  327. }
  328. if(pRiffStream)
  329. {
  330. pRiffStream->Release();
  331. }
  332. return hr;
  333. }
  334. HRESULT CContainer::LoadObjects(IStream *pStream,
  335. IRIFFStream *pRiffStream,
  336. MMCKINFO ckParent,
  337. IDirectMusicLoader *pLoader)
  338. {
  339. MMCKINFO ckNext;
  340. ckNext.ckid = 0;
  341. ckNext.fccType = 0;
  342. HRESULT hr = pRiffStream->Descend(&ckNext, &ckParent, 0);
  343. while(SUCCEEDED(hr))
  344. {
  345. switch(ckNext.ckid)
  346. {
  347. case FOURCC_RIFF:
  348. case FOURCC_LIST:
  349. switch(ckNext.fccType)
  350. {
  351. case DMUS_FOURCC_CONTAINED_OBJECT_LIST :
  352. hr = LoadObject(pStream, pRiffStream, ckNext, pLoader);
  353. break;
  354. }
  355. break;
  356. }
  357. if(SUCCEEDED(hr))
  358. {
  359. hr = pRiffStream->Ascend(&ckNext, 0);
  360. }
  361. if(SUCCEEDED(hr))
  362. {
  363. ckNext.ckid = 0;
  364. ckNext.fccType = 0;
  365. hr = pRiffStream->Descend(&ckNext, &ckParent, 0);
  366. }
  367. }
  368. // DMUS_E_DESCEND_CHUNK_FAIL is returned when the end of the file
  369. // was reached before the desired chunk was found. In the usage
  370. // above we will also get this error if we have finished parsing the file.
  371. // So we need to set hr to S_OK since we are done
  372. hr = (hr == DMUS_E_DESCEND_CHUNK_FAIL) ? S_OK : hr;
  373. return hr;
  374. }
  375. HRESULT CContainer::LoadObject(IStream* pStream,
  376. IRIFFStream *pRiffStream,
  377. MMCKINFO ckParent,
  378. IDirectMusicLoader *pLoader)
  379. {
  380. MMCKINFO ckNext, ckLast;
  381. ckNext.ckid = 0;
  382. ckNext.fccType = 0;
  383. DWORD cbRead;
  384. DWORD cbSize;
  385. DMUS_IO_CONTAINED_OBJECT_HEADER ioHeader;
  386. HRESULT hr = pRiffStream->Descend(&ckNext, &ckParent, 0);
  387. SmartRef::Buffer<WCHAR> wbufAlias;
  388. if(SUCCEEDED(hr))
  389. {
  390. if(ckNext.ckid == DMUS_FOURCC_CONTAINED_ALIAS_CHUNK)
  391. {
  392. if(ckNext.cksize % 2 != 0)
  393. {
  394. assert(false); // should be WCHARs -- two byte pairs
  395. }
  396. else
  397. {
  398. wbufAlias.Alloc(ckNext.cksize / 2);
  399. if (!wbufAlias)
  400. return E_OUTOFMEMORY;
  401. hr = pStream->Read(wbufAlias, ckNext.cksize, &cbRead);
  402. if (FAILED(hr))
  403. return hr;
  404. }
  405. pRiffStream->Ascend(&ckNext, 0);
  406. hr = pRiffStream->Descend(&ckNext, &ckParent, 0);
  407. }
  408. }
  409. if(SUCCEEDED(hr))
  410. {
  411. if(ckNext.ckid != DMUS_FOURCC_CONTAINED_OBJECT_CHUNK)
  412. {
  413. Trace(1,"Invalid object in Container - cobh is not first chunk.\n");
  414. return DMUS_E_INVALID_CONTAINER_OBJECT;
  415. }
  416. cbSize = sizeof(DMUS_IO_CONTAINED_OBJECT_HEADER);
  417. hr = pStream->Read(&ioHeader, cbSize, &cbRead);
  418. if(FAILED(hr))
  419. {
  420. return hr;
  421. }
  422. if(ioHeader.ckid == 0 && ioHeader.fccType == NULL)
  423. {
  424. Trace(1,"Invalid object header in Container.\n");
  425. return DMUS_E_INVALID_CONTAINER_OBJECT;
  426. }
  427. // Move to start of next chunk.
  428. pRiffStream->Ascend(&ckNext, 0);
  429. ckLast = ckNext; // Memorize this position.
  430. hr = pRiffStream->Descend(&ckNext, &ckParent, 0);
  431. while(SUCCEEDED(hr))
  432. {
  433. if((((ckNext.ckid == FOURCC_LIST) || (ckNext.ckid == FOURCC_RIFF))
  434. && ckNext.fccType == ioHeader.fccType) ||
  435. (ckNext.ckid == ioHeader.ckid))
  436. {
  437. // Okay, this is the chunk we are looking for.
  438. // Seek back to start of chunk.
  439. bool fEmbedded = !(ckNext.ckid == FOURCC_LIST && ckNext.fccType == DMUS_FOURCC_REF_LIST);
  440. CContainerItem *pItem = new CContainerItem(fEmbedded);
  441. if (!pItem)
  442. hr = E_OUTOFMEMORY;
  443. else
  444. {
  445. if (fEmbedded)
  446. {
  447. // This is an embedded object. Ascend to the position where from which it will be loaded.
  448. pRiffStream->Ascend(&ckLast, 0);
  449. pItem->m_Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_STREAM;
  450. pItem->m_Desc.guidClass = ioHeader.guidClassID;
  451. pItem->m_Desc.pStream = pStream;
  452. pStream->AddRef();
  453. }
  454. else
  455. {
  456. // This is a reference chunk. Read the object descriptor.
  457. hr = this->ReadReference(pStream, pRiffStream, ckNext, &pItem->m_Desc);
  458. }
  459. if (SUCCEEDED(hr))
  460. {
  461. // We will call SetObject on items in the container here. The items are loaded later.
  462. // This ensures that out-of-order references between objects can be retrieved as the objects
  463. // load themselves.
  464. pLoader->SetObject(&pItem->m_Desc);
  465. if (pItem->m_Desc.dwValidData & DMUS_OBJ_STREAM)
  466. {
  467. // The loader has the stream now so we don't need it any more.
  468. pItem->m_Desc.dwValidData &= ~DMUS_OBJ_STREAM;
  469. SafeRelease(pItem->m_Desc.pStream);
  470. }
  471. pItem->m_pwszAlias = wbufAlias.disown();
  472. m_ItemList.AddTail(pItem);
  473. }
  474. else
  475. delete pItem;
  476. }
  477. }
  478. if(SUCCEEDED(hr))
  479. {
  480. pRiffStream->Ascend(&ckNext, 0);
  481. ckLast = ckNext;
  482. {
  483. ckNext.ckid = 0;
  484. ckNext.fccType = 0;
  485. hr = pRiffStream->Descend(&ckNext, &ckParent, 0);
  486. }
  487. }
  488. }
  489. // DMUS_E_DESCEND_CHUNK_FAIL is returned when the end of the file
  490. // was reached before the desired chunk was found. In the usage
  491. // above we will also get this error if we have finished parsing the file.
  492. // So we need to set hr to S_OK since we are done
  493. hr = (hr == DMUS_E_DESCEND_CHUNK_FAIL) ? S_OK : hr;
  494. }
  495. return hr;
  496. }
  497. HRESULT
  498. CContainer::ReadReference(IStream* pStream,
  499. IRIFFStream *pRiffStream,
  500. MMCKINFO ckParent,
  501. DMUS_OBJECTDESC *pDesc)
  502. {
  503. // I can't believe I'm writing this function! It's copied right out of WaveItem::LoadReference and modified to work here.
  504. // This really aught to be shared code, but the other components all use different stream reader thingies than IRIFFStream
  505. // so that won't work.
  506. if (!pStream || !pRiffStream || !pDesc)
  507. {
  508. assert(false);
  509. return E_INVALIDARG;
  510. }
  511. ZeroAndSize(pDesc);
  512. DWORD cbRead;
  513. MMCKINFO ckNext;
  514. ckNext.ckid = 0;
  515. ckNext.fccType = 0;
  516. DWORD dwSize = 0;
  517. HRESULT hr = S_OK;
  518. while( pRiffStream->Descend( &ckNext, &ckParent, 0 ) == S_OK )
  519. {
  520. switch(ckNext.ckid)
  521. {
  522. case DMUS_FOURCC_REF_CHUNK:
  523. DMUS_IO_REFERENCE ioDMRef;
  524. hr = pStream->Read(&ioDMRef, sizeof(DMUS_IO_REFERENCE), &cbRead);
  525. if(SUCCEEDED(hr))
  526. {
  527. pDesc->guidClass = ioDMRef.guidClassID;
  528. pDesc->dwValidData |= ioDMRef.dwValidData;
  529. pDesc->dwValidData |= DMUS_OBJ_CLASS;
  530. }
  531. break;
  532. case DMUS_FOURCC_GUID_CHUNK:
  533. hr = pStream->Read(&(pDesc->guidObject), sizeof(GUID), &cbRead);
  534. if(SUCCEEDED(hr))
  535. {
  536. pDesc->dwValidData |= DMUS_OBJ_OBJECT;
  537. }
  538. break;
  539. case DMUS_FOURCC_DATE_CHUNK:
  540. hr = pStream->Read(&(pDesc->ftDate), sizeof(FILETIME), &cbRead);
  541. if(SUCCEEDED(hr))
  542. {
  543. pDesc->dwValidData |= DMUS_OBJ_DATE;
  544. }
  545. break;
  546. case DMUS_FOURCC_NAME_CHUNK:
  547. dwSize = min(sizeof(pDesc->wszName), ckNext.cksize);
  548. hr = pStream->Read(pDesc->wszName, dwSize, &cbRead);
  549. if(SUCCEEDED(hr))
  550. {
  551. pDesc->wszName[DMUS_MAX_NAME - 1] = L'\0';
  552. pDesc->dwValidData |= DMUS_OBJ_NAME;
  553. }
  554. break;
  555. case DMUS_FOURCC_FILE_CHUNK:
  556. dwSize = min(sizeof(pDesc->wszFileName), ckNext.cksize);
  557. hr = pStream->Read(pDesc->wszFileName, dwSize, &cbRead);
  558. if(SUCCEEDED(hr))
  559. {
  560. pDesc->wszFileName[DMUS_MAX_FILENAME - 1] = L'\0';
  561. pDesc->dwValidData |= DMUS_OBJ_FILENAME;
  562. }
  563. break;
  564. case DMUS_FOURCC_CATEGORY_CHUNK:
  565. dwSize = min(sizeof(pDesc->wszCategory), ckNext.cksize);
  566. hr = pStream->Read(pDesc->wszCategory, dwSize, &cbRead);
  567. if(SUCCEEDED(hr))
  568. {
  569. pDesc->wszCategory[DMUS_MAX_CATEGORY - 1] = L'\0';
  570. pDesc->dwValidData |= DMUS_OBJ_CATEGORY;
  571. }
  572. break;
  573. case DMUS_FOURCC_VERSION_CHUNK:
  574. DMUS_IO_VERSION ioDMObjVer;
  575. hr = pStream->Read(&ioDMObjVer, sizeof(DMUS_IO_VERSION), &cbRead);
  576. if(SUCCEEDED(hr))
  577. {
  578. pDesc->vVersion.dwVersionMS = ioDMObjVer.dwVersionMS;
  579. pDesc->vVersion.dwVersionLS = ioDMObjVer.dwVersionLS;
  580. pDesc->dwValidData |= DMUS_OBJ_VERSION;
  581. }
  582. break;
  583. default:
  584. break;
  585. }
  586. if(SUCCEEDED(hr) && pRiffStream->Ascend(&ckNext, 0) == S_OK)
  587. {
  588. ckNext.ckid = 0;
  589. ckNext.fccType = 0;
  590. }
  591. else if (SUCCEEDED(hr)) hr = DMUS_E_CANNOTREAD;
  592. }
  593. if (!(pDesc->dwValidData & DMUS_OBJ_NAME) &&
  594. !(pDesc->dwValidData & DMUS_OBJ_FILENAME) &&
  595. !(pDesc->dwValidData & DMUS_OBJ_OBJECT) )
  596. {
  597. Trace(1,"Error: Incomplete object reference in Container - DMRF must specify an object name, filename, or GUID.\n");
  598. hr = DMUS_E_INVALID_CONTAINER_OBJECT;
  599. }
  600. return hr;
  601. }
  602. /////////////////////////////////////////////////////////////////////////////
  603. // IPersist
  604. HRESULT CContainer::GetClassID( CLSID* pClassID )
  605. {
  606. if (m_fZombie)
  607. {
  608. Trace(1, "Error: Call of IDirectMusicContainer::GetClassID after the container has been garbage collected. "
  609. "It is invalid to continue using a container after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  610. "and then calling CollectGarbage or Release on the loader.");
  611. return DMUS_S_GARBAGE_COLLECTED;
  612. }
  613. if (pClassID)
  614. {
  615. *pClassID = CLSID_DirectMusicContainer;
  616. return S_OK;
  617. }
  618. return E_POINTER;
  619. }
  620. /////////////////////////////////////////////////////////////////////////////
  621. // IPersistStream functions
  622. HRESULT CContainer::IsDirty()
  623. {
  624. if (m_fZombie)
  625. {
  626. Trace(1, "Error: Call of IDirectMusicContainer::IsDirty after the container has been garbage collected. "
  627. "It is invalid to continue using a container after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  628. "and then calling CollectGarbage or Release on the loader.");
  629. return DMUS_S_GARBAGE_COLLECTED;
  630. }
  631. return S_FALSE;
  632. }
  633. HRESULT CContainer::Load( IStream* pStream )
  634. {
  635. V_INAME(IPersistStream::Load);
  636. V_INTERFACE(pStream);
  637. if (m_fZombie)
  638. {
  639. Trace(1, "Error: Call of IDirectMusicContainer::Load after the container has been garbage collected. "
  640. "It is invalid to continue using a container after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  641. "and then calling CollectGarbage or Release on the loader.");
  642. return DMUS_S_GARBAGE_COLLECTED;
  643. }
  644. IDirectMusicLoader *pLoader = NULL;
  645. IDirectMusicGetLoader *pGetLoader = NULL;
  646. if (pStream)
  647. {
  648. pStream->QueryInterface(IID_IDirectMusicGetLoader,(void **) &pGetLoader);
  649. if (pGetLoader)
  650. {
  651. pGetLoader->GetLoader(&pLoader);
  652. pGetLoader->Release();
  653. }
  654. }
  655. if (pLoader)
  656. {
  657. HRESULT hr = Load(pStream, pLoader);
  658. pLoader->Release();
  659. return hr;
  660. }
  661. Trace(1, "Error: unable to load container from a stream because it doesn't support the IDirectMusicGetLoader interface.\n");
  662. return DMUS_E_UNSUPPORTED_STREAM;
  663. }
  664. HRESULT CContainer::Save( IStream* pIStream, BOOL fClearDirty )
  665. {
  666. return E_NOTIMPL;
  667. }
  668. HRESULT CContainer::GetSizeMax( ULARGE_INTEGER FAR* pcbSize )
  669. {
  670. return E_NOTIMPL;
  671. }
  672. /////////////////////////////////////////////////////////////////////////////
  673. // IDirectMusicObject
  674. STDMETHODIMP CContainer::GetDescriptor(LPDMUS_OBJECTDESC pDesc)
  675. {
  676. // Argument validation
  677. V_INAME(CContainer::GetDescriptor);
  678. V_PTR_WRITE(pDesc, DMUS_OBJECTDESC);
  679. if (m_fZombie)
  680. {
  681. Trace(1, "Error: Call of IDirectMusicContainer::GetDescriptor after the container has been garbage collected. "
  682. "It is invalid to continue using a container after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  683. "and then calling CollectGarbage or Release on the loader.");
  684. return DMUS_S_GARBAGE_COLLECTED;
  685. }
  686. memset( pDesc, 0, sizeof(DMUS_OBJECTDESC));
  687. pDesc->dwSize = sizeof(DMUS_OBJECTDESC);
  688. pDesc->guidClass = CLSID_DirectMusicContainer;
  689. pDesc->guidObject = m_guidObject;
  690. pDesc->ftDate = m_ftDate;
  691. pDesc->vVersion = m_vVersion;
  692. memcpy( pDesc->wszName, m_wszName, sizeof(m_wszName) );
  693. memcpy( pDesc->wszCategory, m_wszCategory, sizeof(m_wszCategory) );
  694. memcpy( pDesc->wszFileName, m_wszFileName, sizeof(m_wszFileName) );
  695. pDesc->dwValidData = ( m_dwValidData | DMUS_OBJ_CLASS );
  696. return S_OK;
  697. }
  698. STDMETHODIMP CContainer::SetDescriptor(LPDMUS_OBJECTDESC pDesc)
  699. {
  700. // Argument validation
  701. V_INAME(CContainer::SetDescriptor);
  702. V_PTR_READ(pDesc, DMUS_OBJECTDESC);
  703. if (m_fZombie)
  704. {
  705. Trace(1, "Error: Call of IDirectMusicContainer::SetDescriptor after the container has been garbage collected. "
  706. "It is invalid to continue using a container after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  707. "and then calling CollectGarbage or Release on the loader.");
  708. return DMUS_S_GARBAGE_COLLECTED;
  709. }
  710. HRESULT hr = S_OK;
  711. DWORD dw = 0;
  712. if( pDesc->dwValidData & DMUS_OBJ_OBJECT )
  713. {
  714. m_guidObject = pDesc->guidObject;
  715. dw |= DMUS_OBJ_OBJECT;
  716. }
  717. if( pDesc->dwValidData & DMUS_OBJ_NAME )
  718. {
  719. memcpy( m_wszName, pDesc->wszName, sizeof(WCHAR)*DMUS_MAX_NAME );
  720. dw |= DMUS_OBJ_NAME;
  721. }
  722. if( pDesc->dwValidData & DMUS_OBJ_CATEGORY )
  723. {
  724. memcpy( m_wszCategory, pDesc->wszCategory, sizeof(WCHAR)*DMUS_MAX_CATEGORY );
  725. dw |= DMUS_OBJ_CATEGORY;
  726. }
  727. if( ( pDesc->dwValidData & DMUS_OBJ_FILENAME ) ||
  728. ( pDesc->dwValidData & DMUS_OBJ_FULLPATH ) )
  729. {
  730. memcpy( m_wszFileName, pDesc->wszFileName, sizeof(WCHAR)*DMUS_MAX_FILENAME );
  731. dw |= (pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH));
  732. }
  733. if( pDesc->dwValidData & DMUS_OBJ_VERSION )
  734. {
  735. m_vVersion = pDesc->vVersion;
  736. dw |= DMUS_OBJ_VERSION;
  737. }
  738. if( pDesc->dwValidData & DMUS_OBJ_DATE )
  739. {
  740. m_ftDate = pDesc->ftDate;
  741. dw |= DMUS_OBJ_DATE;
  742. }
  743. m_dwValidData |= dw;
  744. if( pDesc->dwValidData & (~dw) )
  745. {
  746. hr = S_FALSE; // there were extra fields we didn't parse;
  747. pDesc->dwValidData = dw;
  748. }
  749. return hr;
  750. }
  751. STDMETHODIMP CContainer::ParseDescriptor(LPSTREAM pStream, LPDMUS_OBJECTDESC pDesc)
  752. {
  753. V_INAME(CContainer::ParseDescriptor);
  754. V_INTERFACE(pStream);
  755. V_STRUCTPTR_WRITE(pDesc, DMUS_OBJECTDESC);
  756. if (m_fZombie)
  757. {
  758. Trace(1, "Error: Call of IDirectMusicContainer::ParseDescriptor after the container has been garbage collected. "
  759. "It is invalid to continue using a container after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
  760. "and then calling CollectGarbage or Release on the loader.");
  761. return DMUS_S_GARBAGE_COLLECTED;
  762. }
  763. IRIFFStream *pRiffStream = NULL;
  764. HRESULT hr = AllocRIFFStream(pStream, &pRiffStream);
  765. if (FAILED(hr))
  766. return hr;
  767. MMCKINFO ckMain;
  768. ckMain.fccType = DMUS_FOURCC_CONTAINER_FORM;
  769. hr = pRiffStream->Descend(&ckMain, NULL, MMIO_FINDRIFF);
  770. if(FAILED(hr))
  771. {
  772. pRiffStream->Release();
  773. return hr;
  774. }
  775. pDesc->dwValidData = DMUS_OBJ_CLASS;
  776. pDesc->guidClass = CLSID_DirectMusicContainer;
  777. MMCKINFO ckNext;
  778. MMCKINFO ckUNFO;
  779. DWORD cbRead;
  780. DWORD cbSize;
  781. ckNext.ckid = 0;
  782. ckNext.fccType = 0;
  783. hr = pRiffStream->Descend(&ckNext, &ckMain, 0);
  784. while(SUCCEEDED(hr))
  785. {
  786. switch(ckNext.ckid)
  787. {
  788. case DMUS_FOURCC_GUID_CHUNK:
  789. cbSize = sizeof(GUID);
  790. if( ckNext.cksize == cbSize )
  791. {
  792. hr = pStream->Read( &pDesc->guidObject, cbSize, &cbRead );
  793. if( SUCCEEDED(hr) && (cbRead == cbSize) )
  794. {
  795. pDesc->dwValidData |= DMUS_OBJ_OBJECT;
  796. }
  797. }
  798. break;
  799. case DMUS_FOURCC_VERSION_CHUNK:
  800. hr = pStream->Read(&pDesc->vVersion, sizeof(DMUS_IO_VERSION), &cbRead);
  801. if(SUCCEEDED(hr))
  802. {
  803. pDesc->dwValidData |= DMUS_OBJ_VERSION;
  804. }
  805. break;
  806. case DMUS_FOURCC_CATEGORY_CHUNK:
  807. {
  808. cbSize = min(sizeof(pDesc->wszCategory), ckNext.cksize);
  809. hr = pStream->Read(pDesc->wszCategory, cbSize, &cbRead);
  810. if(SUCCEEDED(hr))
  811. {
  812. pDesc->dwValidData |= DMUS_OBJ_CATEGORY;
  813. }
  814. }
  815. break;
  816. case DMUS_FOURCC_DATE_CHUNK:
  817. hr = pStream->Read(&(pDesc->ftDate), sizeof(FILETIME), &cbRead);
  818. if(SUCCEEDED(hr))
  819. {
  820. pDesc->dwValidData |= DMUS_OBJ_DATE;
  821. }
  822. break;
  823. case FOURCC_LIST:
  824. switch(ckNext.fccType)
  825. {
  826. case DMUS_FOURCC_UNFO_LIST:
  827. while( pRiffStream->Descend( &ckUNFO, &ckNext, 0 ) == S_OK )
  828. {
  829. switch( ckUNFO.ckid )
  830. {
  831. case DMUS_FOURCC_UNAM_CHUNK:
  832. {
  833. cbSize = min(sizeof(pDesc->wszName), ckUNFO.cksize);
  834. hr = pStream->Read(&pDesc->wszName, cbSize, &cbRead);
  835. if(SUCCEEDED(hr))
  836. {
  837. pDesc->dwValidData |= DMUS_OBJ_NAME;
  838. }
  839. break;
  840. }
  841. default:
  842. break;
  843. }
  844. pRiffStream->Ascend( &ckUNFO, 0 );
  845. }
  846. break;
  847. }
  848. break;
  849. }
  850. if(SUCCEEDED(hr))
  851. {
  852. hr = pRiffStream->Ascend(&ckNext, 0);
  853. }
  854. if(SUCCEEDED(hr))
  855. {
  856. ckNext.ckid = 0;
  857. ckNext.fccType = 0;
  858. hr = pRiffStream->Descend(&ckNext, &ckMain, 0);
  859. }
  860. }
  861. // DMUS_E_DESCEND_CHUNK_FAIL is returned when the end of the file
  862. // was reached before the desired chunk was found. In the usage
  863. // above we will also get this error if we have finished parsing the file.
  864. // So we need to set hr to S_OK since we are done
  865. hr = (hr == DMUS_E_DESCEND_CHUNK_FAIL) ? S_OK : hr;
  866. if(pRiffStream)
  867. {
  868. pRiffStream->Release();
  869. }
  870. return hr;
  871. }