windows-nt/Source/XPSP1/NT/multimedia/directx/dmusic/dmime/smartref.cpp
2020-09-26 16:20:57 +08:00

563 lines
13 KiB
C++

// Copyright (c) 1999 Microsoft Corporation. All rights reserved.
//
// Simple helper classes that use the "resource acquisition is initialization" technique.
// In English, this means they acquire a reference to a resource and the resource is automatically
// released in the destructor.
//
#include "smartref.h"
using namespace SmartRef;
#include <xutility>
#include "miscutil.h"
#include "dmusicf.h"
//////////////////////////////////////////////////////////////////////
// AString
AString::AString(const char *psz, UINT cch)
{
assert(psz);
m_psz = new char[cch + 1];
if (m_psz)
{
strncpy(m_psz, psz, cch);
m_psz[cch] = L'\0';
}
}
AString &
AString::operator =(const char *psz)
{
if (m_psz)
{
delete[] m_psz;
m_psz = NULL;
}
if (psz)
{
m_psz = new char[strlen(psz) + 1];
if (m_psz)
strcpy(m_psz, psz);
}
return *this;
}
AString &AString::Assign(const char *psz, UINT cch)
{
assert(psz);
if (m_psz)
{
delete[] m_psz;
m_psz = NULL;
}
m_psz = new char[cch + 1];
if (m_psz)
{
strncpy(m_psz, psz, cch);
m_psz[cch] = L'\0';
}
return *this;
}
AString &
AString::AssignFromW(const WCHAR *psz)
{
if (m_psz)
{
delete[] m_psz;
m_psz = NULL;
}
if (psz)
{
int cch = WideCharToMultiByte(CP_ACP, 0, psz, -1, NULL, 0, NULL, NULL);
if (cch)
{
m_psz = new char[cch];
if (m_psz)
{
cch = WideCharToMultiByte(CP_ACP, 0, psz, -1, m_psz, cch, NULL, NULL);
if (!cch)
{
assert(false);
delete[] m_psz;
m_psz = NULL;
}
}
}
}
return *this;
}
//////////////////////////////////////////////////////////////////////
// WString
WString &
WString::operator =(const WCHAR *psz)
{
if (m_psz)
{
delete[] m_psz;
m_psz = NULL;
}
if (psz)
{
m_psz = new WCHAR[wcslen(psz) + 1];
if (m_psz)
wcscpy(m_psz, psz);
}
return *this;
}
WString &WString::Assign(const WCHAR *psz, UINT cch)
{
assert(psz);
if (m_psz)
{
delete[] m_psz;
m_psz = NULL;
}
m_psz = new WCHAR[cch + 1];
if (m_psz)
{
wcsncpy(m_psz, psz, cch);
m_psz[cch] = L'\0';
}
return *this;
}
WString &
WString::AssignFromA(const char *psz)
{
if (m_psz)
{
delete[] m_psz;
m_psz = NULL;
}
if (psz)
{
int cch = MultiByteToWideChar(CP_ACP, 0, psz, -1, NULL, 0);
if (cch)
{
m_psz = new WCHAR[cch];
if (m_psz)
{
cch = MultiByteToWideChar(CP_ACP, 0, psz, -1, m_psz, cch);
if (!cch)
{
assert(false);
delete[] m_psz;
m_psz = NULL;
}
}
}
}
return *this;
}
//////////////////////////////////////////////////////////////////////
// RiffIter
RiffIter::RiffIter(IStream *pStream)
: m_hr(S_OK),
m_pIStream(pStream),
m_pIDMStream(NULL),
m_fParent(false)
{
m_pIStream->AddRef();
ZeroMemory(&m_ckParent, sizeof(m_ckParent));
ZeroMemory(&m_ckChild, sizeof(m_ckChild));
m_hr = ::AllocDirectMusicStream(m_pIStream, &m_pIDMStream);
if (FAILED(m_hr))
return;
m_hr = m_pIDMStream->Descend(&m_ckChild, NULL, 0);
}
RiffIter::~RiffIter()
{
if (!m_fParent)
{
SafeRelease(m_pIStream);
SafeRelease(m_pIDMStream);
}
}
RiffIter
&RiffIter::operator ++()
{
if (validate())
return *this;
m_hr = m_pIDMStream->Ascend(&m_ckChild, 0);
if (FAILED(m_hr))
return *this;
m_ckChild.ckid = 0;
m_ckChild.fccType = 0;
m_hr = m_pIDMStream->Descend(&m_ckChild, m_fParent ? &m_ckParent : NULL, 0);
return *this;
}
RiffIter
&RiffIter::Find(RiffType t, FOURCC idFind)
{
if (validate())
return *this;
while (*this && (type() != t || id() != idFind))
++*this;
return *this;
}
HRESULT
RiffIter::ReadChunk(
void *pv,
UINT cb)
{
if (type() != Chunk)
{
assert(false);
return DMUS_E_CANNOTREAD;
}
ZeroMemory(pv, cb);
DWORD cbRead = 0;
DWORD cbSize = std::_cpp_min<DWORD>(cb, m_ckChild.cksize);
HRESULT hr = m_pIStream->Read(pv, cbSize, &cbRead);
if (FAILED(hr) || cbRead != cbSize)
{
Trace(1, "Error: Unable to read file.\n");
hr = DMUS_E_CANNOTREAD;
}
return hr;
}
HRESULT
RiffIter::ReadArrayChunk(
DWORD cbSize,
void **ppv,
int *pcRecords)
{
// zero the out params
*ppv = NULL;
*pcRecords = 0;
// get the size of the chunk and its records
UINT cbChunk = size();
if (cbChunk < sizeof(DWORD))
{
assert(false);
return E_FAIL;
}
DWORD cbChunkRecord = 0;
HRESULT hr = RiffIterReadChunk(*this, &cbChunkRecord);
if (FAILED(hr))
return hr;
cbChunk -= sizeof(DWORD);
if (cbChunk % cbChunkRecord != 0)
{
// array is not divisible by size of records!
assert(false);
return E_FAIL;
}
UINT cRecords = cbChunk / cbChunkRecord;
// Get the whole rest of the chunk
PtrArray<char> sprgChunk = new char[cbChunk];
if (!sprgChunk)
return E_OUTOFMEMORY;
hr = ReadChunk(sprgChunk, cbChunk);
if (FAILED(hr))
return hr;
// Return the chunk and its info.
if (cbChunkRecord == cbSize)
{
// Great! Return the chunk as is.
*ppv = sprgChunk.disown();
}
else
{
// make an array of the requested size
char *pArray = new char[cbSize * cRecords];
if (!pArray)
return E_OUTOFMEMORY;
ZeroMemory(pArray, cbSize * cRecords);
// copy each record
char *pRec = sprgChunk; // iterate reading each record of the chunk
char *pEnd = pRec + cbChunkRecord * cRecords; // stop before this (nonexistant) record
char *pOut = pArray; // iterate writing into the array
while (pRec < pEnd)
{
memcpy(pOut, pRec, std::_cpp_min<DWORD>(cbChunkRecord, cbSize));
pRec += cbChunkRecord;
pOut += cbSize;
}
*ppv = pArray;
}
*pcRecords = cRecords;
return hr;
}
HRESULT RiffIter::FindAndGetEmbeddedObject(
RiffType t,
FOURCC idFind,
HRESULT hrOnNotFound,
IDirectMusicLoader *pLoader,
REFCLSID rclsid,
REFIID riid,
LPVOID *ppv)
{
if (validate() || !pLoader || !ppv)
{
assert(false);
return E_FAIL;
}
*ppv = NULL;
MMCKINFO ckLast;
ZeroMemory(&ckLast, sizeof(ckLast));
while (*this && (type() != t || id() != idFind))
{
ckLast = m_ckChild;
++*this;
}
if (!*this)
return hrOnNotFound;
// Ascend in such a way that the stream can be used to find this chunk.
m_hr = m_pIDMStream->Ascend(&ckLast, 0);
if (FAILED(m_hr))
return m_hr;
// Call GetObject using the stream
DMUS_OBJECTDESC desc;
ZeroAndSize(&desc);
desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_STREAM;
desc.guidClass = rclsid;
desc.pStream = m_pIStream;
HRESULT hrLoad = pLoader->GetObject(&desc, riid, ppv);
// Descend again to leave the stream at the next chunk
m_ckChild.ckid = 0;
m_ckChild.fccType = 0;
m_hr = m_pIDMStream->Descend(&m_ckChild, m_fParent ? &m_ckParent : NULL, 0);
HRESULT hrDescend = this->hr();
if (FAILED(hrDescend))
{
// Give precedence to reporting failure in the stream even though getting the
// object succeeded before the failure.
if (*ppv)
{
IUnknown *punk = static_cast<IUnknown *>(*ppv);
if (punk)
punk->Release();
*ppv = NULL;
}
return hrDescend;
}
else
{
return hrLoad;
}
}
HRESULT
RiffIter::ReadReference(DMUS_OBJECTDESC *pDESC)
{
HRESULT hr = S_OK;
assert(this->type() == List && this->id() == DMUS_FOURCC_REF_LIST);
ZeroAndSize(pDESC);
for (RiffIter ri = this->Descend(); ri; ++ri)
{
switch (ri.id())
{
case DMUS_FOURCC_REF_CHUNK:
DMUS_IO_REFERENCE ioDMRef;
hr = SmartRef::RiffIterReadChunk(ri, &ioDMRef);
if (SUCCEEDED(hr))
{
pDESC->guidClass = ioDMRef.guidClassID;
pDESC->dwValidData |= ioDMRef.dwValidData;
pDESC->dwValidData |= DMUS_OBJ_CLASS;
}
break;
case DMUS_FOURCC_GUID_CHUNK:
hr = SmartRef::RiffIterReadChunk(ri, &pDESC->guidObject);
if (SUCCEEDED(hr))
pDESC->dwValidData |= DMUS_OBJ_OBJECT;
break;
case DMUS_FOURCC_DATE_CHUNK:
hr = SmartRef::RiffIterReadChunk(ri, &pDESC->ftDate);
if (SUCCEEDED(hr))
pDESC->dwValidData |= DMUS_OBJ_DATE;
break;
case DMUS_FOURCC_NAME_CHUNK:
hr = SmartRef::RiffIterReadChunk(ri, &pDESC->wszName);
if (SUCCEEDED(hr))
{
pDESC->wszName[DMUS_MAX_NAME - 1] = L'\0';
pDESC->dwValidData |= DMUS_OBJ_NAME;
}
break;
case DMUS_FOURCC_FILE_CHUNK:
hr = SmartRef::RiffIterReadChunk(ri, &pDESC->wszFileName);
if (SUCCEEDED(hr))
{
pDESC->wszFileName[DMUS_MAX_FILENAME - 1] = L'\0';
pDESC->dwValidData |= DMUS_OBJ_FILENAME;
}
break;
case DMUS_FOURCC_CATEGORY_CHUNK:
hr = SmartRef::RiffIterReadChunk(ri, &pDESC->wszCategory);
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 = SmartRef::RiffIterReadChunk(ri, &ioDMObjVer);
if (SUCCEEDED(hr))
{
pDESC->vVersion.dwVersionMS = ioDMObjVer.dwVersionMS;
pDESC->vVersion.dwVersionLS = ioDMObjVer.dwVersionLS;
pDESC->dwValidData |= DMUS_OBJ_VERSION;
}
else
{
hr = E_FAIL;
}
break;
default:
break;
}
}
return ri.hr();
}
HRESULT RiffIter::LoadObjectInfo(ObjectInfo *pObjInfo, RiffType rtypeStop, FOURCC ridStop)
{
assert(pObjInfo);
pObjInfo->Clear();
HRESULT hr = S_OK;
if (!(*this))
return this->hr();
for ( ; *this; ++(*this))
{
RiffType rtype = type();
FOURCC fcc = id();
if (rtype == rtypeStop && fcc == ridStop)
return S_OK;
if (rtype == SmartRef::RiffIter::Chunk)
{
if (fcc == DMUS_FOURCC_GUID_CHUNK)
hr = SmartRef::RiffIterReadChunk(*this, &pObjInfo->guid);
else if (fcc == DMUS_FOURCC_VERSION_CHUNK)
hr = SmartRef::RiffIterReadChunk(*this, &pObjInfo->vVersion);
}
else if (rtype == SmartRef::RiffIter::List)
{
if (fcc == DMUS_FOURCC_UNFO_LIST)
{
RiffIter riUnfo = this->Descend();
if (!riUnfo)
return riUnfo.hr();
if (riUnfo.Find(SmartRef::RiffIter::Chunk, DMUS_FOURCC_UNAM_CHUNK))
{
hr = riUnfo.ReadTextTrunc(pObjInfo->wszName, DMUS_MAX_NAME);
if (FAILED(hr))
return hr;
}
}
}
if (FAILED(hr))
return hr;
}
Trace(1, "Error: Unable to read file.\n");
return E_FAIL;
}
HRESULT RiffIter::ReadText(WCHAR **ppwsz)
{
DWORD dwSize = this->size();
if (dwSize % 2 != 0)
{
assert(false);
return E_FAIL;
}
*ppwsz = new WCHAR[dwSize / 2];
if (!*ppwsz)
return E_OUTOFMEMORY;
HRESULT hr = this->ReadChunk(*ppwsz, dwSize);
return hr;
}
HRESULT RiffIter::ReadTextTrunc(WCHAR *pwsz, UINT cbBufSize)
{
DWORD dwSize = this->size();
if (dwSize % 2 != 0)
{
assert(false);
return E_FAIL;
}
HRESULT hr = this->ReadChunk(pwsz, std::_MIN<DWORD>(dwSize, (cbBufSize - 1) * 2));
pwsz[cbBufSize - 1] = L'\0';
return hr;
}
RiffIter::RiffIter(const RiffIter &other, MMCKINFO ckParent)
: m_hr(S_OK),
m_pIStream(other.m_pIStream),
m_pIDMStream(other.m_pIDMStream),
m_fParent(true),
m_ckParent(ckParent)
{
other.validate();
ZeroMemory(&m_ckChild, sizeof(m_ckChild));
m_hr = m_pIDMStream->Descend(&m_ckChild, &m_ckParent, 0);
}