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.

562 lines
13 KiB

  1. // Copyright (c) 1999 Microsoft Corporation. All rights reserved.
  2. //
  3. // Simple helper classes that use the "resource acquisition is initialization" technique.
  4. // In English, this means they acquire a reference to a resource and the resource is automatically
  5. // released in the destructor.
  6. //
  7. #include "smartref.h"
  8. using namespace SmartRef;
  9. #include <xutility>
  10. #include "miscutil.h"
  11. #include "dmusicf.h"
  12. //////////////////////////////////////////////////////////////////////
  13. // AString
  14. AString::AString(const char *psz, UINT cch)
  15. {
  16. assert(psz);
  17. m_psz = new char[cch + 1];
  18. if (m_psz)
  19. {
  20. strncpy(m_psz, psz, cch);
  21. m_psz[cch] = L'\0';
  22. }
  23. }
  24. AString &
  25. AString::operator =(const char *psz)
  26. {
  27. if (m_psz)
  28. {
  29. delete[] m_psz;
  30. m_psz = NULL;
  31. }
  32. if (psz)
  33. {
  34. m_psz = new char[strlen(psz) + 1];
  35. if (m_psz)
  36. strcpy(m_psz, psz);
  37. }
  38. return *this;
  39. }
  40. AString &AString::Assign(const char *psz, UINT cch)
  41. {
  42. assert(psz);
  43. if (m_psz)
  44. {
  45. delete[] m_psz;
  46. m_psz = NULL;
  47. }
  48. m_psz = new char[cch + 1];
  49. if (m_psz)
  50. {
  51. strncpy(m_psz, psz, cch);
  52. m_psz[cch] = L'\0';
  53. }
  54. return *this;
  55. }
  56. AString &
  57. AString::AssignFromW(const WCHAR *psz)
  58. {
  59. if (m_psz)
  60. {
  61. delete[] m_psz;
  62. m_psz = NULL;
  63. }
  64. if (psz)
  65. {
  66. int cch = WideCharToMultiByte(CP_ACP, 0, psz, -1, NULL, 0, NULL, NULL);
  67. if (cch)
  68. {
  69. m_psz = new char[cch];
  70. if (m_psz)
  71. {
  72. cch = WideCharToMultiByte(CP_ACP, 0, psz, -1, m_psz, cch, NULL, NULL);
  73. if (!cch)
  74. {
  75. assert(false);
  76. delete[] m_psz;
  77. m_psz = NULL;
  78. }
  79. }
  80. }
  81. }
  82. return *this;
  83. }
  84. //////////////////////////////////////////////////////////////////////
  85. // WString
  86. WString &
  87. WString::operator =(const WCHAR *psz)
  88. {
  89. if (m_psz)
  90. {
  91. delete[] m_psz;
  92. m_psz = NULL;
  93. }
  94. if (psz)
  95. {
  96. m_psz = new WCHAR[wcslen(psz) + 1];
  97. if (m_psz)
  98. wcscpy(m_psz, psz);
  99. }
  100. return *this;
  101. }
  102. WString &WString::Assign(const WCHAR *psz, UINT cch)
  103. {
  104. assert(psz);
  105. if (m_psz)
  106. {
  107. delete[] m_psz;
  108. m_psz = NULL;
  109. }
  110. m_psz = new WCHAR[cch + 1];
  111. if (m_psz)
  112. {
  113. wcsncpy(m_psz, psz, cch);
  114. m_psz[cch] = L'\0';
  115. }
  116. return *this;
  117. }
  118. WString &
  119. WString::AssignFromA(const char *psz)
  120. {
  121. if (m_psz)
  122. {
  123. delete[] m_psz;
  124. m_psz = NULL;
  125. }
  126. if (psz)
  127. {
  128. int cch = MultiByteToWideChar(CP_ACP, 0, psz, -1, NULL, 0);
  129. if (cch)
  130. {
  131. m_psz = new WCHAR[cch];
  132. if (m_psz)
  133. {
  134. cch = MultiByteToWideChar(CP_ACP, 0, psz, -1, m_psz, cch);
  135. if (!cch)
  136. {
  137. assert(false);
  138. delete[] m_psz;
  139. m_psz = NULL;
  140. }
  141. }
  142. }
  143. }
  144. return *this;
  145. }
  146. //////////////////////////////////////////////////////////////////////
  147. // RiffIter
  148. RiffIter::RiffIter(IStream *pStream)
  149. : m_hr(S_OK),
  150. m_pIStream(pStream),
  151. m_pIDMStream(NULL),
  152. m_fParent(false)
  153. {
  154. m_pIStream->AddRef();
  155. ZeroMemory(&m_ckParent, sizeof(m_ckParent));
  156. ZeroMemory(&m_ckChild, sizeof(m_ckChild));
  157. m_hr = ::AllocDirectMusicStream(m_pIStream, &m_pIDMStream);
  158. if (FAILED(m_hr))
  159. return;
  160. m_hr = m_pIDMStream->Descend(&m_ckChild, NULL, 0);
  161. }
  162. RiffIter::~RiffIter()
  163. {
  164. if (!m_fParent)
  165. {
  166. SafeRelease(m_pIStream);
  167. SafeRelease(m_pIDMStream);
  168. }
  169. }
  170. RiffIter
  171. &RiffIter::operator ++()
  172. {
  173. if (validate())
  174. return *this;
  175. m_hr = m_pIDMStream->Ascend(&m_ckChild, 0);
  176. if (FAILED(m_hr))
  177. return *this;
  178. m_ckChild.ckid = 0;
  179. m_ckChild.fccType = 0;
  180. m_hr = m_pIDMStream->Descend(&m_ckChild, m_fParent ? &m_ckParent : NULL, 0);
  181. return *this;
  182. }
  183. RiffIter
  184. &RiffIter::Find(RiffType t, FOURCC idFind)
  185. {
  186. if (validate())
  187. return *this;
  188. while (*this && (type() != t || id() != idFind))
  189. ++*this;
  190. return *this;
  191. }
  192. HRESULT
  193. RiffIter::ReadChunk(
  194. void *pv,
  195. UINT cb)
  196. {
  197. if (type() != Chunk)
  198. {
  199. assert(false);
  200. return DMUS_E_CANNOTREAD;
  201. }
  202. ZeroMemory(pv, cb);
  203. DWORD cbRead = 0;
  204. DWORD cbSize = std::_cpp_min<DWORD>(cb, m_ckChild.cksize);
  205. HRESULT hr = m_pIStream->Read(pv, cbSize, &cbRead);
  206. if (FAILED(hr) || cbRead != cbSize)
  207. {
  208. Trace(1, "Error: Unable to read file.\n");
  209. hr = DMUS_E_CANNOTREAD;
  210. }
  211. return hr;
  212. }
  213. HRESULT
  214. RiffIter::ReadArrayChunk(
  215. DWORD cbSize,
  216. void **ppv,
  217. int *pcRecords)
  218. {
  219. // zero the out params
  220. *ppv = NULL;
  221. *pcRecords = 0;
  222. // get the size of the chunk and its records
  223. UINT cbChunk = size();
  224. if (cbChunk < sizeof(DWORD))
  225. {
  226. assert(false);
  227. return E_FAIL;
  228. }
  229. DWORD cbChunkRecord = 0;
  230. HRESULT hr = RiffIterReadChunk(*this, &cbChunkRecord);
  231. if (FAILED(hr))
  232. return hr;
  233. cbChunk -= sizeof(DWORD);
  234. if (cbChunk % cbChunkRecord != 0)
  235. {
  236. // array is not divisible by size of records!
  237. assert(false);
  238. return E_FAIL;
  239. }
  240. UINT cRecords = cbChunk / cbChunkRecord;
  241. // Get the whole rest of the chunk
  242. PtrArray<char> sprgChunk = new char[cbChunk];
  243. if (!sprgChunk)
  244. return E_OUTOFMEMORY;
  245. hr = ReadChunk(sprgChunk, cbChunk);
  246. if (FAILED(hr))
  247. return hr;
  248. // Return the chunk and its info.
  249. if (cbChunkRecord == cbSize)
  250. {
  251. // Great! Return the chunk as is.
  252. *ppv = sprgChunk.disown();
  253. }
  254. else
  255. {
  256. // make an array of the requested size
  257. char *pArray = new char[cbSize * cRecords];
  258. if (!pArray)
  259. return E_OUTOFMEMORY;
  260. ZeroMemory(pArray, cbSize * cRecords);
  261. // copy each record
  262. char *pRec = sprgChunk; // iterate reading each record of the chunk
  263. char *pEnd = pRec + cbChunkRecord * cRecords; // stop before this (nonexistant) record
  264. char *pOut = pArray; // iterate writing into the array
  265. while (pRec < pEnd)
  266. {
  267. memcpy(pOut, pRec, std::_cpp_min<DWORD>(cbChunkRecord, cbSize));
  268. pRec += cbChunkRecord;
  269. pOut += cbSize;
  270. }
  271. *ppv = pArray;
  272. }
  273. *pcRecords = cRecords;
  274. return hr;
  275. }
  276. HRESULT RiffIter::FindAndGetEmbeddedObject(
  277. RiffType t,
  278. FOURCC idFind,
  279. HRESULT hrOnNotFound,
  280. IDirectMusicLoader *pLoader,
  281. REFCLSID rclsid,
  282. REFIID riid,
  283. LPVOID *ppv)
  284. {
  285. if (validate() || !pLoader || !ppv)
  286. {
  287. assert(false);
  288. return E_FAIL;
  289. }
  290. *ppv = NULL;
  291. MMCKINFO ckLast;
  292. ZeroMemory(&ckLast, sizeof(ckLast));
  293. while (*this && (type() != t || id() != idFind))
  294. {
  295. ckLast = m_ckChild;
  296. ++*this;
  297. }
  298. if (!*this)
  299. return hrOnNotFound;
  300. // Ascend in such a way that the stream can be used to find this chunk.
  301. m_hr = m_pIDMStream->Ascend(&ckLast, 0);
  302. if (FAILED(m_hr))
  303. return m_hr;
  304. // Call GetObject using the stream
  305. DMUS_OBJECTDESC desc;
  306. ZeroAndSize(&desc);
  307. desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_STREAM;
  308. desc.guidClass = rclsid;
  309. desc.pStream = m_pIStream;
  310. HRESULT hrLoad = pLoader->GetObject(&desc, riid, ppv);
  311. // Descend again to leave the stream at the next chunk
  312. m_ckChild.ckid = 0;
  313. m_ckChild.fccType = 0;
  314. m_hr = m_pIDMStream->Descend(&m_ckChild, m_fParent ? &m_ckParent : NULL, 0);
  315. HRESULT hrDescend = this->hr();
  316. if (FAILED(hrDescend))
  317. {
  318. // Give precedence to reporting failure in the stream even though getting the
  319. // object succeeded before the failure.
  320. if (*ppv)
  321. {
  322. IUnknown *punk = static_cast<IUnknown *>(*ppv);
  323. if (punk)
  324. punk->Release();
  325. *ppv = NULL;
  326. }
  327. return hrDescend;
  328. }
  329. else
  330. {
  331. return hrLoad;
  332. }
  333. }
  334. HRESULT
  335. RiffIter::ReadReference(DMUS_OBJECTDESC *pDESC)
  336. {
  337. HRESULT hr = S_OK;
  338. assert(this->type() == List && this->id() == DMUS_FOURCC_REF_LIST);
  339. ZeroAndSize(pDESC);
  340. for (RiffIter ri = this->Descend(); ri; ++ri)
  341. {
  342. switch (ri.id())
  343. {
  344. case DMUS_FOURCC_REF_CHUNK:
  345. DMUS_IO_REFERENCE ioDMRef;
  346. hr = SmartRef::RiffIterReadChunk(ri, &ioDMRef);
  347. if (SUCCEEDED(hr))
  348. {
  349. pDESC->guidClass = ioDMRef.guidClassID;
  350. pDESC->dwValidData |= ioDMRef.dwValidData;
  351. pDESC->dwValidData |= DMUS_OBJ_CLASS;
  352. }
  353. break;
  354. case DMUS_FOURCC_GUID_CHUNK:
  355. hr = SmartRef::RiffIterReadChunk(ri, &pDESC->guidObject);
  356. if (SUCCEEDED(hr))
  357. pDESC->dwValidData |= DMUS_OBJ_OBJECT;
  358. break;
  359. case DMUS_FOURCC_DATE_CHUNK:
  360. hr = SmartRef::RiffIterReadChunk(ri, &pDESC->ftDate);
  361. if (SUCCEEDED(hr))
  362. pDESC->dwValidData |= DMUS_OBJ_DATE;
  363. break;
  364. case DMUS_FOURCC_NAME_CHUNK:
  365. hr = SmartRef::RiffIterReadChunk(ri, &pDESC->wszName);
  366. if (SUCCEEDED(hr))
  367. {
  368. pDESC->wszName[DMUS_MAX_NAME - 1] = L'\0';
  369. pDESC->dwValidData |= DMUS_OBJ_NAME;
  370. }
  371. break;
  372. case DMUS_FOURCC_FILE_CHUNK:
  373. hr = SmartRef::RiffIterReadChunk(ri, &pDESC->wszFileName);
  374. if (SUCCEEDED(hr))
  375. {
  376. pDESC->wszFileName[DMUS_MAX_FILENAME - 1] = L'\0';
  377. pDESC->dwValidData |= DMUS_OBJ_FILENAME;
  378. }
  379. break;
  380. case DMUS_FOURCC_CATEGORY_CHUNK:
  381. hr = SmartRef::RiffIterReadChunk(ri, &pDESC->wszCategory);
  382. if (SUCCEEDED(hr))
  383. {
  384. pDESC->wszCategory[DMUS_MAX_CATEGORY - 1] = L'\0';
  385. pDESC->dwValidData |= DMUS_OBJ_CATEGORY;
  386. }
  387. break;
  388. case DMUS_FOURCC_VERSION_CHUNK:
  389. DMUS_IO_VERSION ioDMObjVer;
  390. hr = SmartRef::RiffIterReadChunk(ri, &ioDMObjVer);
  391. if (SUCCEEDED(hr))
  392. {
  393. pDESC->vVersion.dwVersionMS = ioDMObjVer.dwVersionMS;
  394. pDESC->vVersion.dwVersionLS = ioDMObjVer.dwVersionLS;
  395. pDESC->dwValidData |= DMUS_OBJ_VERSION;
  396. }
  397. else
  398. {
  399. hr = E_FAIL;
  400. }
  401. break;
  402. default:
  403. break;
  404. }
  405. }
  406. return ri.hr();
  407. }
  408. HRESULT RiffIter::LoadObjectInfo(ObjectInfo *pObjInfo, RiffType rtypeStop, FOURCC ridStop)
  409. {
  410. assert(pObjInfo);
  411. pObjInfo->Clear();
  412. HRESULT hr = S_OK;
  413. if (!(*this))
  414. return this->hr();
  415. for ( ; *this; ++(*this))
  416. {
  417. RiffType rtype = type();
  418. FOURCC fcc = id();
  419. if (rtype == rtypeStop && fcc == ridStop)
  420. return S_OK;
  421. if (rtype == SmartRef::RiffIter::Chunk)
  422. {
  423. if (fcc == DMUS_FOURCC_GUID_CHUNK)
  424. hr = SmartRef::RiffIterReadChunk(*this, &pObjInfo->guid);
  425. else if (fcc == DMUS_FOURCC_VERSION_CHUNK)
  426. hr = SmartRef::RiffIterReadChunk(*this, &pObjInfo->vVersion);
  427. }
  428. else if (rtype == SmartRef::RiffIter::List)
  429. {
  430. if (fcc == DMUS_FOURCC_UNFO_LIST)
  431. {
  432. RiffIter riUnfo = this->Descend();
  433. if (!riUnfo)
  434. return riUnfo.hr();
  435. if (riUnfo.Find(SmartRef::RiffIter::Chunk, DMUS_FOURCC_UNAM_CHUNK))
  436. {
  437. hr = riUnfo.ReadTextTrunc(pObjInfo->wszName, DMUS_MAX_NAME);
  438. if (FAILED(hr))
  439. return hr;
  440. }
  441. }
  442. }
  443. if (FAILED(hr))
  444. return hr;
  445. }
  446. Trace(1, "Error: Unable to read file.\n");
  447. return E_FAIL;
  448. }
  449. HRESULT RiffIter::ReadText(WCHAR **ppwsz)
  450. {
  451. DWORD dwSize = this->size();
  452. if (dwSize % 2 != 0)
  453. {
  454. assert(false);
  455. return E_FAIL;
  456. }
  457. *ppwsz = new WCHAR[dwSize / 2];
  458. if (!*ppwsz)
  459. return E_OUTOFMEMORY;
  460. HRESULT hr = this->ReadChunk(*ppwsz, dwSize);
  461. return hr;
  462. }
  463. HRESULT RiffIter::ReadTextTrunc(WCHAR *pwsz, UINT cbBufSize)
  464. {
  465. DWORD dwSize = this->size();
  466. if (dwSize % 2 != 0)
  467. {
  468. assert(false);
  469. return E_FAIL;
  470. }
  471. HRESULT hr = this->ReadChunk(pwsz, std::_MIN<DWORD>(dwSize, (cbBufSize - 1) * 2));
  472. pwsz[cbBufSize - 1] = L'\0';
  473. return hr;
  474. }
  475. RiffIter::RiffIter(const RiffIter &other, MMCKINFO ckParent)
  476. : m_hr(S_OK),
  477. m_pIStream(other.m_pIStream),
  478. m_pIDMStream(other.m_pIDMStream),
  479. m_fParent(true),
  480. m_ckParent(ckParent)
  481. {
  482. other.validate();
  483. ZeroMemory(&m_ckChild, sizeof(m_ckChild));
  484. m_hr = m_pIDMStream->Descend(&m_ckChild, &m_ckParent, 0);
  485. }