|
|
// Copyright (c) 1998-2001 Microsoft Corporation
// Object.cpp : Implementations of CObject and CClass
#include "dmusici.h"
#include "loader.h"
#include "debug.h"
#include "miscutil.h"
#include <strsafe.h>
#ifdef UNDER_CE
#include "dragon.h"
#else
extern BOOL g_fIsUnicode; #endif
CDescriptor::CDescriptor()
{ m_fCSInitialized = FALSE; InitializeCriticalSection(&m_CriticalSection); // Note: on pre-Blackcomb OS's, this call can raise an exception; if it
// ever pops in stress, we can add an exception handler and retry loop.
m_fCSInitialized = TRUE;
m_llMemLength = 0; m_pbMemData = NULL; // Null pointer to memory.
m_dwValidData = 0; // Flags indicating which of above is valid.
m_guidObject = GUID_NULL; // Unique ID for this object.
m_guidClass = GUID_NULL; // GUID for the class of object.
ZeroMemory( &m_ftDate, sizeof(FILETIME) ); // File date of object.
ZeroMemory( &m_vVersion, sizeof(DMUS_VERSION) ); // Version, as set by authoring tool.
m_pwzName = NULL; // Name of object.
m_pwzCategory = NULL; // Category for object (optional).
m_pwzFileName = NULL; // File path.
m_dwFileSize = 0; // Size of file.
m_pIStream = NULL; m_liStartPosition.QuadPart = 0; }
CDescriptor::~CDescriptor()
{ if (m_fCSInitialized) { // If critical section never initialized, never got a chance
// to do any other initializations
//
if (m_pwzFileName) delete[] m_pwzFileName; if (m_pwzCategory) delete[] m_pwzCategory; if (m_pwzName) delete[] m_pwzName; if (m_pIStream) m_pIStream->Release(); DeleteCriticalSection(&m_CriticalSection); } }
void CDescriptor::ClearName()
{ if (m_pwzName) delete[] m_pwzName; m_pwzName = NULL; m_dwValidData &= ~DMUS_OBJ_NAME; }
void CDescriptor::SetName(WCHAR *pwzName)
{ if(pwzName == NULL) { return; }
HRESULT hr = S_OK; WCHAR wszName[DMUS_MAX_NAME] = L"";
ClearName(); hr = StringCchCopyW(wszName, DMUS_MAX_NAME, pwzName); if(SUCCEEDED(hr)) { size_t cLen = wcslen(wszName); m_pwzName = new WCHAR[cLen + 1]; if(m_pwzName == NULL) { hr = E_OUTOFMEMORY; } if(SUCCEEDED(hr)) { wcsncpy(m_pwzName, wszName, cLen + 1); } } if(SUCCEEDED(hr)) { m_dwValidData |= DMUS_OBJ_NAME; } else { m_dwValidData &= ~DMUS_OBJ_NAME; } }
void CDescriptor::ClearCategory()
{ if (m_pwzCategory) delete[] m_pwzCategory; m_pwzCategory = NULL; m_dwValidData &= ~DMUS_OBJ_CATEGORY; }
void CDescriptor::SetCategory(WCHAR* pwzCategory)
{ if(pwzCategory == NULL) { return; }
HRESULT hr = S_OK; WCHAR wszCategory[DMUS_MAX_CATEGORY] = L"";
ClearCategory(); hr = StringCchCopyW(wszCategory, DMUS_MAX_CATEGORY, pwzCategory);
if(SUCCEEDED(hr)) { size_t cLen = wcslen(wszCategory); m_pwzCategory = new WCHAR[cLen + 1];
if(m_pwzCategory == NULL) { hr = E_OUTOFMEMORY; } if(SUCCEEDED(hr)) { wcsncpy(m_pwzCategory, wszCategory, cLen + 1); } }
if(SUCCEEDED(hr)) { m_dwValidData |= DMUS_OBJ_CATEGORY; } else { m_dwValidData &= ~DMUS_OBJ_CATEGORY; } }
void CDescriptor::ClearFileName()
{ if (m_pwzFileName) delete[] m_pwzFileName; m_pwzFileName = NULL; m_dwValidData &= ~DMUS_OBJ_FILENAME; }
// return S_FALSE if the filename is already set to this
HRESULT CDescriptor::SetFileName(WCHAR *pwzFileName)
{ if(pwzFileName == NULL) { return E_POINTER; }
HRESULT hr = E_FAIL; WCHAR wszFileName[DMUS_MAX_FILENAME] = L"";
// Make a safe copy of the passed string
hr = StringCchCopyW(wszFileName, DMUS_MAX_FILENAME, pwzFileName); if(FAILED(hr)) { return E_INVALIDARG; }
// We return without touching the valid data flags if we fail here
if( m_pwzFileName ) { if( !_wcsicmp( m_pwzFileName, wszFileName )) { return S_FALSE; } }
// This is actually unnecessary since we're returning on failure above
// But then that code might change. So to keep it absolutely clear...
if(SUCCEEDED(hr)) { ClearFileName();
size_t cLen = wcslen(wszFileName); m_pwzFileName = new WCHAR[cLen + 1]; if (m_pwzFileName == NULL) { hr = E_OUTOFMEMORY; }
if(SUCCEEDED(hr)) { hr = StringCchCopyW(m_pwzFileName, cLen + 1, wszFileName); } }
if(SUCCEEDED(hr)) { m_dwValidData |= DMUS_OBJ_FILENAME; } else { m_dwValidData &= ~DMUS_OBJ_FILENAME; }
return hr; }
void CDescriptor::ClearIStream()
{ EnterCriticalSection(&m_CriticalSection); if (m_pIStream) { m_pIStream->Release(); } m_pIStream = NULL; m_liStartPosition.QuadPart = 0; m_dwValidData &= ~DMUS_OBJ_STREAM; LeaveCriticalSection(&m_CriticalSection); }
void CDescriptor::SetIStream(IStream *pIStream)
{ EnterCriticalSection(&m_CriticalSection); ClearIStream();
m_pIStream = pIStream;
if (m_pIStream) { ULARGE_INTEGER libNewPosition; m_liStartPosition.QuadPart = 0; m_pIStream->Seek( m_liStartPosition, STREAM_SEEK_CUR, &libNewPosition ); m_liStartPosition.QuadPart = libNewPosition.QuadPart; m_pIStream->AddRef(); m_dwValidData |= DMUS_OBJ_STREAM; } LeaveCriticalSection(&m_CriticalSection); }
BOOL CDescriptor::IsExtension(WCHAR *pwzExtension)
{ if (pwzExtension && m_pwzFileName) { DWORD dwX; DWORD dwLen = wcslen(m_pwzFileName); for (dwX = 0; dwX < dwLen; dwX++) { if (m_pwzFileName[dwX] == L'.') break; } dwX++; if (dwX < dwLen) { return !_wcsicmp(pwzExtension,&m_pwzFileName[dwX]); } } return FALSE; }
void CDescriptor::Get(LPDMUS_OBJECTDESC pDesc)
{ if(pDesc == NULL) { return; }
// Don't return the IStream insterface. Once set, this becomes private to the loader.
pDesc->dwValidData = m_dwValidData & ~DMUS_OBJ_STREAM;
pDesc->guidObject = m_guidObject; pDesc->guidClass = m_guidClass; pDesc->ftDate = m_ftDate; pDesc->vVersion = m_vVersion; pDesc->llMemLength = m_llMemLength; pDesc->pbMemData = m_pbMemData; if (m_pwzName) { wcsncpy( pDesc->wszName, m_pwzName, DMUS_MAX_NAME ); } if (m_pwzCategory) { wcsncpy( pDesc->wszCategory,m_pwzCategory, DMUS_MAX_CATEGORY ); } if (m_pwzFileName) { wcsncpy( pDesc->wszFileName, m_pwzFileName, DMUS_MAX_FILENAME); } }
void CDescriptor::Set(LPDMUS_OBJECTDESC pDesc)
{ m_dwValidData = pDesc->dwValidData; m_guidObject = pDesc->guidObject; m_guidClass = pDesc->guidClass; m_ftDate = pDesc->ftDate; m_vVersion = pDesc->vVersion; m_llMemLength = pDesc->llMemLength; m_pbMemData = pDesc->pbMemData; ClearName(); if (pDesc->dwValidData & DMUS_OBJ_NAME) { pDesc->wszName[DMUS_MAX_NAME - 1] = 0; // Force string length, in case of error.
SetName(pDesc->wszName); } ClearCategory(); if (pDesc->dwValidData & DMUS_OBJ_CATEGORY) { pDesc->wszCategory[DMUS_MAX_CATEGORY - 1] = 0; // Force string length, in case of error.
SetCategory(pDesc->wszCategory); } ClearFileName(); if (pDesc->dwValidData & DMUS_OBJ_FILENAME) { pDesc->wszFileName[DMUS_MAX_FILENAME - 1] = 0; // Force string length, in case of error.
SetFileName(pDesc->wszFileName); } ClearIStream(); if (pDesc->dwValidData & DMUS_OBJ_STREAM) { SetIStream(pDesc->pStream); } }
void CDescriptor::Copy(CDescriptor *pDesc)
{ m_dwValidData = pDesc->m_dwValidData; m_guidObject = pDesc->m_guidObject; m_guidClass = pDesc->m_guidClass; m_ftDate = pDesc->m_ftDate; m_vVersion = pDesc->m_vVersion; m_llMemLength = pDesc->m_llMemLength; m_pbMemData = pDesc->m_pbMemData; ClearName(); if (pDesc->m_dwValidData & DMUS_OBJ_NAME) { SetName(pDesc->m_pwzName); } ClearCategory(); if (pDesc->m_dwValidData & DMUS_OBJ_CATEGORY) { SetCategory(pDesc->m_pwzCategory); } ClearFileName(); if (pDesc->m_dwValidData & DMUS_OBJ_FILENAME) { SetFileName(pDesc->m_pwzFileName); } ClearIStream(); if (pDesc->m_dwValidData & DMUS_OBJ_STREAM) { SetIStream(pDesc->m_pIStream); } }
void CDescriptor::Merge(CDescriptor *pSource)
{ if (pSource->m_dwValidData & DMUS_OBJ_OBJECT) { m_dwValidData |= DMUS_OBJ_OBJECT; m_guidObject = pSource->m_guidObject; } if (pSource->m_dwValidData & DMUS_OBJ_CLASS) { m_dwValidData |= DMUS_OBJ_CLASS; m_guidClass = pSource->m_guidClass; } if (pSource->m_dwValidData & DMUS_OBJ_NAME) { m_dwValidData |= DMUS_OBJ_NAME; SetName(pSource->m_pwzName); } if (pSource->m_dwValidData & DMUS_OBJ_CATEGORY) { m_dwValidData |= DMUS_OBJ_CATEGORY; SetCategory(pSource->m_pwzCategory); } if (pSource->m_dwValidData & DMUS_OBJ_VERSION) { m_dwValidData |= DMUS_OBJ_VERSION; m_vVersion = pSource->m_vVersion; } if (pSource->m_dwValidData & DMUS_OBJ_DATE) { m_dwValidData |= DMUS_OBJ_DATE; m_ftDate = pSource->m_ftDate; } if (pSource->m_dwValidData & DMUS_OBJ_FILENAME) { if (!(m_dwValidData & DMUS_OBJ_FILENAME)) { if (SUCCEEDED(SetFileName(pSource->m_pwzFileName))) { m_dwValidData |= (pSource->m_dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_URL)); } } } if (pSource->m_dwValidData & DMUS_OBJ_MEMORY) { m_pbMemData = pSource->m_pbMemData; m_llMemLength = pSource->m_llMemLength; if (m_llMemLength && m_pbMemData) { m_dwValidData |= DMUS_OBJ_MEMORY; } else { m_dwValidData &= ~DMUS_OBJ_MEMORY; } } if (pSource->m_dwValidData & DMUS_OBJ_STREAM) { SetIStream(pSource->m_pIStream); } }
CObject::CObject(CClass *pClass)
{ m_dwScanBits = 0; m_pClass = pClass; m_pIDMObject = NULL; m_pvecReferences = NULL; }
CObject::CObject(CClass *pClass, CDescriptor *pDesc)
{ m_dwScanBits = 0; m_pClass = pClass; m_pIDMObject = NULL; m_ObjectDesc.Copy(pDesc); m_ObjectDesc.m_dwValidData &= ~DMUS_OBJ_LOADED; if (!(m_ObjectDesc.m_dwValidData & DMUS_OBJ_CLASS)) { m_ObjectDesc.m_guidClass = pClass->m_ClassDesc.m_guidClass; m_ObjectDesc.m_dwValidData |= (pClass->m_ClassDesc.m_dwValidData & DMUS_OBJ_CLASS); } m_pvecReferences = NULL; }
CObject::~CObject()
{ if (m_pIDMObject) { m_pIDMObject->Release(); m_pIDMObject = NULL; } delete m_pvecReferences; }
HRESULT CObject::Parse()
{ if (m_ObjectDesc.m_dwValidData & DMUS_OBJ_FILENAME) { return ParseFromFile(); } else if (m_ObjectDesc.m_dwValidData & DMUS_OBJ_MEMORY) { return ParseFromMemory(); } else if (m_ObjectDesc.m_dwValidData & DMUS_OBJ_STREAM) { return ParseFromStream(); } assert(false); return E_FAIL; }
HRESULT CObject::ParseFromFile()
{ HRESULT hr; IDirectMusicObject *pIObject; hr = CoCreateInstance(m_ObjectDesc.m_guidClass, NULL,CLSCTX_INPROC_SERVER,IID_IDirectMusicObject, (void **) &pIObject); if (SUCCEEDED(hr)) { WCHAR wzFullPath[DMUS_MAX_FILENAME]; ZeroMemory( wzFullPath, sizeof(WCHAR) * DMUS_MAX_FILENAME ); CFileStream *pStream = new CFileStream ( m_pClass->m_pLoader ); if (pStream) { if (m_ObjectDesc.m_dwValidData & DMUS_OBJ_FULLPATH) { wcsncpy(wzFullPath, m_ObjectDesc.m_pwzFileName, DMUS_MAX_FILENAME); } else { m_pClass->GetPath(wzFullPath); wcsncat(wzFullPath, m_ObjectDesc.m_pwzFileName, DMUS_MAX_FILENAME - wcslen(wzFullPath) - 1); } hr = pStream->Open(wzFullPath,GENERIC_READ); if (SUCCEEDED(hr)) { DMUS_OBJECTDESC DESC; memset((void *)&DESC,0,sizeof(DESC)); DESC.dwSize = sizeof (DMUS_OBJECTDESC); hr = pIObject->ParseDescriptor(pStream,&DESC); if (SUCCEEDED(hr)) { CDescriptor ParseDesc; ParseDesc.Set(&DESC); m_ObjectDesc.Merge(&ParseDesc); } } pStream->Release(); } pIObject->Release(); } return hr; }
HRESULT CObject::ParseFromMemory()
{ HRESULT hr; IDirectMusicObject *pIObject; hr = CoCreateInstance(m_ObjectDesc.m_guidClass, NULL,CLSCTX_INPROC_SERVER,IID_IDirectMusicObject, (void **) &pIObject); if (SUCCEEDED(hr)) { CMemStream *pStream = new CMemStream ( m_pClass->m_pLoader ); if (pStream) { hr = pStream->Open(m_ObjectDesc.m_pbMemData,m_ObjectDesc.m_llMemLength); if (SUCCEEDED(hr)) { DMUS_OBJECTDESC DESC; memset((void *)&DESC,0,sizeof(DESC)); DESC.dwSize = sizeof (DMUS_OBJECTDESC); hr = pIObject->ParseDescriptor(pStream,&DESC); if (SUCCEEDED(hr)) { CDescriptor ParseDesc; ParseDesc.Set(&DESC); m_ObjectDesc.Merge(&ParseDesc); } } pStream->Release(); } pIObject->Release(); } return hr; }
HRESULT CObject::ParseFromStream()
{ HRESULT hr; IDirectMusicObject *pIObject; hr = CoCreateInstance(m_ObjectDesc.m_guidClass, NULL,CLSCTX_INPROC_SERVER,IID_IDirectMusicObject, (void **) &pIObject); if (SUCCEEDED(hr)) { CStream *pStream = new CStream ( m_pClass->m_pLoader ); if (pStream) { hr = pStream->Open(m_ObjectDesc.m_pIStream, m_ObjectDesc.m_liStartPosition); if (SUCCEEDED(hr)) { DMUS_OBJECTDESC DESC; memset((void *)&DESC,0,sizeof(DESC)); DESC.dwSize = sizeof (DMUS_OBJECTDESC); hr = pIObject->ParseDescriptor(pStream,&DESC); if (SUCCEEDED(hr)) { CDescriptor ParseDesc; ParseDesc.Set(&DESC); m_ObjectDesc.Merge(&ParseDesc); } } pStream->Release(); } pIObject->Release(); } return hr; }
// Record that this object can be garbage collected and prepare to store its references.
// Must be called before any of CObject's other routines.
HRESULT CObject::GC_Collectable()
{ m_dwScanBits |= SCAN_GC; assert(!m_pvecReferences);
m_pvecReferences = new SmartRef::Vector<CObject*>; if (!m_pvecReferences) return E_OUTOFMEMORY; return S_OK; }
HRESULT CObject::GC_AddReference(CObject *pObject)
{ if(pObject == NULL) { return E_POINTER; }
assert(m_dwScanBits & SCAN_GC && m_pvecReferences);
// don't track references to objects that aren't garbage collected
if (!(pObject->m_dwScanBits & SCAN_GC)) return S_OK;
UINT uiPosNext = m_pvecReferences->size(); for (UINT i = 0; i < uiPosNext; ++i) { if ((*m_pvecReferences)[i] == pObject) return S_OK; }
if (!m_pvecReferences->AccessTo(uiPosNext)) return E_OUTOFMEMORY; (*m_pvecReferences)[uiPosNext] = pObject; return S_OK; }
HRESULT CObject::GC_RemoveReference(CObject *pObject)
{ assert(m_dwScanBits & SCAN_GC && m_pvecReferences);
SmartRef::Vector<CObject*> &vecRefs = *m_pvecReferences; UINT iEnd = vecRefs.size(); for (UINT i = 0; i < iEnd; ++i) { if (vecRefs[i] == pObject) { // Remove by clearing the pointer.
// The open slot will be compacted during garbage collection (GC_Mark).
vecRefs[i] = NULL; return S_OK; } } return S_FALSE; }
// Helper method used to implement ReleaseObject.
HRESULT CObject::GC_RemoveAndDuplicateInParentList() { CObject* pObjectToFind = NULL; HRESULT hr = m_pClass->FindObject(&m_ObjectDesc, &pObjectToFind, this); if (SUCCEEDED(hr) && pObjectToFind) { m_pClass->GC_Replace(this, NULL); } else { CObject *pObjectUnloaded = new CObject(m_pClass, &m_ObjectDesc); if (!pObjectUnloaded) { return E_OUTOFMEMORY; }
m_pClass->GC_Replace(this, pObjectUnloaded); } return S_OK; }
HRESULT CObject::Load()
{ // See if we have one of the fields we need to load
if (!(m_ObjectDesc.m_dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_MEMORY | DMUS_OBJ_STREAM))) { Trace(1, "Error: GetObject failed because the requested object was not already cached and the supplied desciptor did not specify a source to load the object from (DMUS_OBJ_FILENAME, DMUS_OBJ_MEMORY, or DMUS_OBJ_STREAM)."); return DMUS_E_LOADER_NOFILENAME; }
// Create the object
SmartRef::ComPtr<IDirectMusicObject> scomIObject = NULL; HRESULT hr = CoCreateInstance(m_ObjectDesc.m_guidClass, NULL, CLSCTX_INPROC_SERVER, IID_IDirectMusicObject, reinterpret_cast<void**>(&scomIObject)); if (FAILED(hr)) return hr;
// Create the stream the object will load from
SmartRef::ComPtr<IStream> scomIStream; if (m_ObjectDesc.m_dwValidData & DMUS_OBJ_FILENAME) { WCHAR wzFullPath[DMUS_MAX_FILENAME]; ZeroMemory( wzFullPath, sizeof(WCHAR) * DMUS_MAX_FILENAME ); CFileStream *pStream = new CFileStream ( m_pClass->m_pLoader ); if (!pStream) return E_OUTOFMEMORY; scomIStream = pStream;
if (m_ObjectDesc.m_dwValidData & DMUS_OBJ_FULLPATH) { wcsncpy(wzFullPath, m_ObjectDesc.m_pwzFileName, DMUS_MAX_FILENAME); } else { m_pClass->GetPath(wzFullPath); wcsncat(wzFullPath,m_ObjectDesc.m_pwzFileName, DMUS_MAX_FILENAME - wcslen(wzFullPath) - 1); } hr = pStream->Open(wzFullPath,GENERIC_READ); if (FAILED(hr)) return hr; } else if (m_ObjectDesc.m_dwValidData & DMUS_OBJ_MEMORY) { CMemStream *pStream = new CMemStream ( m_pClass->m_pLoader ); if (!pStream) return E_OUTOFMEMORY; scomIStream = pStream; hr = pStream->Open(m_ObjectDesc.m_pbMemData, m_ObjectDesc.m_llMemLength); if (FAILED(hr)) return hr; } else if (m_ObjectDesc.m_dwValidData & DMUS_OBJ_STREAM) { CStream *pStream = new CStream ( m_pClass->m_pLoader ); if (!pStream) return E_OUTOFMEMORY; scomIStream = pStream; hr = pStream->Open(m_ObjectDesc.m_pIStream, m_ObjectDesc.m_liStartPosition); if (FAILED(hr)) return hr; }
// Load the object
IPersistStream* pIPS = NULL; hr = scomIObject->QueryInterface( IID_IPersistStream, (void**)&pIPS ); if (FAILED(hr)) return hr; // Save the new object. Needs to be done before loading because of circular references. While this object
// loads it could get other objects and those other objects could need to get this object.
SafeRelease(m_pIDMObject); m_pIDMObject = scomIObject.disown(); hr = pIPS->Load( scomIStream ); pIPS->Release(); if (FAILED(hr)) { // Clear the object we set above.
SafeRelease(m_pIDMObject); return hr; }
// Merge in descriptor information from the object
CDescriptor Desc; DMUS_OBJECTDESC DESC; memset((void *)&DESC,0,sizeof(DESC)); DESC.dwSize = sizeof (DMUS_OBJECTDESC); m_pIDMObject->GetDescriptor(&DESC); Desc.Set(&DESC); m_ObjectDesc.Merge(&Desc); m_ObjectDesc.m_dwValidData |= DMUS_OBJ_LOADED; m_ObjectDesc.Get(&DESC); m_pIDMObject->SetDescriptor(&DESC); return hr; }
// Collect everything that is unmarked.
void CObjectList::GC_Sweep(BOOL bOnlyScripts)
{ // sweep through looking for unmarked GC objects
CObject *pObjectPrev = NULL; CObject *pObjectNext = NULL; for (CObject *pObject = this->GetHead(); pObject; pObject = pObjectNext) { // get the next item now since we could be messing with the list
pObjectNext = pObject->GetNext();
bool fRemoved = false; if(bOnlyScripts && pObject->m_ObjectDesc.m_guidClass != CLSID_DirectMusicScript) { pObjectPrev = pObject; continue; }
if (pObject->m_dwScanBits & SCAN_GC) { if (!(pObject->m_dwScanBits & SCAN_GC_MARK)) { // the object is unused
// Zombie it to break any cyclic references
IDirectMusicObject *pIDMO = pObject->m_pIDMObject; if (pIDMO) { IDirectMusicObjectP *pIDMO8 = NULL; HRESULT hr = pIDMO->QueryInterface(IID_IDirectMusicObjectP, reinterpret_cast<void**>(&pIDMO8)); if (SUCCEEDED(hr)) { pIDMO8->Zombie(); pIDMO8->Release(); }
#ifdef DBG
DebugTrace(4, SUCCEEDED(hr) ? " *%08X Zombied\n" : " *%08X no IDirectMusicObjectP interface\n", pObject); #endif
}
// remove it from the list
if (pObjectPrev) pObjectPrev->Remove(pObject); else this->RemoveHead(); delete pObject; fRemoved = true; } else { // clear mark for next time
pObject->m_dwScanBits &= ~SCAN_GC_MARK; } }
if (!fRemoved) pObjectPrev = pObject; } }
CClass::CClass(CLoader *pLoader)
{ assert(pLoader); m_fDirSearched = FALSE; m_pLoader = pLoader; m_fKeepObjects = pLoader->m_fKeepObjects; m_dwLastIndex = NULL; m_pLastObject = NULL; }
CClass::CClass(CLoader *pLoader, CDescriptor *pDesc)
{ assert(pLoader); assert(pDesc);
m_fDirSearched = FALSE; m_pLoader = pLoader; m_fKeepObjects = pLoader->m_fKeepObjects; m_dwLastIndex = NULL; m_pLastObject = NULL;
// Set up this class's descritor with just the class id.
m_ClassDesc.m_guidClass = pDesc->m_guidClass; m_ClassDesc.m_dwValidData = DMUS_OBJ_CLASS; }
CClass::~CClass()
{ ClearObjects(FALSE,NULL); }
void CClass::ClearObjects(BOOL fKeepCache, WCHAR *pwzExtension)
// Clear objects from the class list, optionally keep
// cached objects or objects that are not of the requested extension.
{ m_fDirSearched = FALSE; CObjectList KeepList; // Use to store objects to keep.
while (!m_ObjectList.IsEmpty()) { CObject *pObject = m_ObjectList.RemoveHead(); DMUS_OBJECTDESC DESC; pObject->m_ObjectDesc.Get(&DESC); // If the keepCache flag is set, we want to hang on to the object
// if it is GM.dls, an object that's currently cached, or
// an object with a different extension from what we are looking for.
if (fKeepCache && ((DESC.guidObject == GUID_DefaultGMCollection) #ifdef DRAGON
|| (DESC.guidObject == GUID_DefaultGMDrums) #endif
|| pObject->m_pIDMObject || !pObject->m_ObjectDesc.IsExtension(pwzExtension))) { KeepList.AddHead(pObject); } else { delete pObject; } } // Now put cached objects back in list.
while (!KeepList.IsEmpty()) { CObject *pObject = KeepList.RemoveHead(); m_ObjectList.AddHead(pObject); } m_pLastObject = NULL; }
HRESULT CClass::FindObject(CDescriptor *pDesc,CObject ** ppObject, CObject *pNotThis)
{ if(pDesc == NULL) { return E_POINTER; }
DWORD dwSearchBy = pDesc->m_dwValidData; CObject *pObject = NULL;
if (dwSearchBy & DMUS_OBJ_OBJECT) { pObject = m_ObjectList.GetHead(); for (;pObject != NULL; pObject = pObject->GetNext()) { if (pObject == pNotThis) continue; if (pObject->m_ObjectDesc.m_dwValidData & DMUS_OBJ_OBJECT) { if (pObject->m_ObjectDesc.m_guidObject == pDesc->m_guidObject) { *ppObject = pObject; return S_OK; } } } } if (dwSearchBy & DMUS_OBJ_MEMORY) { pObject = m_ObjectList.GetHead(); for (;pObject != NULL; pObject = pObject->GetNext()) { if (pObject == pNotThis) continue; if (pObject->m_ObjectDesc.m_dwValidData & DMUS_OBJ_MEMORY) { if (pObject->m_ObjectDesc.m_pbMemData == pDesc->m_pbMemData) { *ppObject = pObject; return S_OK; } } } } if (dwSearchBy & DMUS_OBJ_STREAM) { pObject = m_ObjectList.GetHead(); for (;pObject != NULL; pObject = pObject->GetNext()) { if (pObject == pNotThis) continue; if (pObject->m_ObjectDesc.m_dwValidData & DMUS_OBJ_STREAM) { if (pObject->m_ObjectDesc.m_pIStream == pDesc->m_pIStream) { *ppObject = pObject; return S_OK; } } } } if ((dwSearchBy & DMUS_OBJ_FILENAME) && (dwSearchBy & DMUS_OBJ_FULLPATH)) { pObject = m_ObjectList.GetHead(); for (;pObject != NULL; pObject = pObject->GetNext()) { if (pObject == pNotThis) continue; if ((pObject->m_ObjectDesc.m_dwValidData & DMUS_OBJ_FILENAME) && (pObject->m_ObjectDesc.m_dwValidData & DMUS_OBJ_FULLPATH)) { if (!_wcsicmp(pObject->m_ObjectDesc.m_pwzFileName, pDesc->m_pwzFileName)) { *ppObject = pObject; return S_OK; } } } } if ((dwSearchBy & DMUS_OBJ_NAME) && (dwSearchBy & DMUS_OBJ_CATEGORY)) { pObject = m_ObjectList.GetHead(); for (;pObject != NULL; pObject = pObject->GetNext()) { if (pObject == pNotThis) continue; if ((pObject->m_ObjectDesc.m_dwValidData & DMUS_OBJ_NAME) && (pObject->m_ObjectDesc.m_dwValidData & DMUS_OBJ_CATEGORY)) { if (!_wcsicmp(pObject->m_ObjectDesc.m_pwzCategory,pDesc->m_pwzCategory)) { if (!_wcsicmp(pObject->m_ObjectDesc.m_pwzName, pDesc->m_pwzName)) { *ppObject = pObject; return S_OK; } } } } } if (dwSearchBy & DMUS_OBJ_NAME) { pObject = m_ObjectList.GetHead(); for (;pObject != NULL; pObject = pObject->GetNext()) { if (pObject == pNotThis) continue; if (pObject->m_ObjectDesc.m_dwValidData & DMUS_OBJ_NAME) { if (!_wcsicmp(pObject->m_ObjectDesc.m_pwzName, pDesc->m_pwzName)) { *ppObject = pObject; return S_OK; } } } } if (dwSearchBy & DMUS_OBJ_FILENAME) { pObject = m_ObjectList.GetHead(); for (;pObject != NULL; pObject = pObject->GetNext()) { if (pObject == pNotThis) continue; if (pObject->m_ObjectDesc.m_dwValidData & DMUS_OBJ_FILENAME) { if ((dwSearchBy & DMUS_OBJ_FULLPATH) == (pObject->m_ObjectDesc.m_dwValidData & DMUS_OBJ_FULLPATH)) { if (!_wcsicmp(pObject->m_ObjectDesc.m_pwzFileName, pDesc->m_pwzFileName)) { *ppObject = pObject; return S_OK; } } else { WCHAR *pC1 = pObject->m_ObjectDesc.m_pwzFileName; WCHAR *pC2 = pDesc->m_pwzFileName; if (dwSearchBy & DMUS_OBJ_FULLPATH) { pC1 = wcsrchr(pObject->m_ObjectDesc.m_pwzFileName, L'\\'); } else { pC2 = wcsrchr(pDesc->m_pwzFileName, '\\'); } if (pC1 && pC2) { if (!_wcsicmp(pC1,pC2)) { *ppObject = pObject; return S_OK; } } } } } }
*ppObject = NULL; return DMUS_E_LOADER_OBJECTNOTFOUND; }
HRESULT CClass::EnumerateObjects(DWORD dwIndex,CDescriptor *pDesc)
{ if(pDesc == NULL) { return E_POINTER; }
if (m_fDirSearched == FALSE) { // SearchDirectory();
} if ((dwIndex < m_dwLastIndex) || (m_pLastObject == NULL)) { m_dwLastIndex = 0; m_pLastObject = m_ObjectList.GetHead(); } while (m_dwLastIndex < dwIndex) { if (!m_pLastObject) break; m_dwLastIndex++; m_pLastObject = m_pLastObject->GetNext(); } if (m_pLastObject) { pDesc->Copy(&m_pLastObject->m_ObjectDesc); return S_OK; } return S_FALSE; }
HRESULT CClass::GetPath(WCHAR* pwzPath)
{ if(pwzPath == NULL) { return E_POINTER; }
if (m_ClassDesc.m_dwValidData & DMUS_OBJ_FILENAME) { wcsncpy(pwzPath, m_ClassDesc.m_pwzFileName, DMUS_MAX_FILENAME); return S_OK; } else { return m_pLoader->GetPath(pwzPath); } }
// returns S_FALSE if the search directory is already set to this.
HRESULT CClass::SetSearchDirectory(WCHAR * pwzPath,BOOL fClear)
{ if(pwzPath == NULL) { return E_POINTER; }
HRESULT hr;
hr = m_ClassDesc.SetFileName(pwzPath); if (SUCCEEDED(hr)) { m_ClassDesc.m_dwValidData |= DMUS_OBJ_FULLPATH; } if (fClear) { CObjectList KeepList; // Use to store objects to keep.
while (!m_ObjectList.IsEmpty()) { CObject *pObject = m_ObjectList.RemoveHead(); if (pObject->m_pIDMObject) { KeepList.AddHead(pObject); } else { // check for the special case of the default gm collection.
// don't clear that one out.
DMUS_OBJECTDESC DESC; pObject->m_ObjectDesc.Get(&DESC); if( DESC.guidObject == GUID_DefaultGMCollection ) { KeepList.AddHead(pObject); } else { delete pObject; } } } // Now put cached objects back in list.
while (!KeepList.IsEmpty()) { CObject *pObject = KeepList.RemoveHead(); m_ObjectList.AddHead(pObject); } m_pLastObject = NULL; } return hr; }
HRESULT CClass::GetObject(CDescriptor *pDesc, CObject ** ppObject)
{ if(pDesc == NULL) { return E_POINTER; } HRESULT hr = FindObject(pDesc,ppObject); if (SUCCEEDED(hr)) // Okay, found object in list.
{ return hr; } *ppObject = new CObject (this, pDesc); if (*ppObject) { m_ObjectList.AddHead(*ppObject); return S_OK; } return E_OUTOFMEMORY; }
void CClass::RemoveObject(CObject* pRemoveObject) // Remove an object from the class list
{ CObjectList KeepList; // Use to store objects to keep.
while (!m_ObjectList.IsEmpty()) { CObject *pObject = m_ObjectList.RemoveHead(); if( pObject == pRemoveObject ) { delete pObject; // we can assume no duplicates, and we should avoid comparing the deleted
// object to the remainder of the list
break; } else { KeepList.AddHead(pObject); } } // Now put cached objects back in list.
while (!KeepList.IsEmpty()) { CObject *pObject = KeepList.RemoveHead(); m_ObjectList.AddHead(pObject); } m_pLastObject = NULL; }
HRESULT CClass::ClearCache(bool fClearStreams)
{ CObject *pObject = m_ObjectList.GetHead(); CObject *pObjectPrev = NULL; // remember the previous object -- needed to quickly remove the current object from the list
CObject *pObjectNext = NULL; // remember the next object -- needed because the current object may be removed from the list
for (;pObject;pObject = pObjectNext) { if (fClearStreams) pObject->m_ObjectDesc.ClearIStream(); pObjectNext = pObject->GetNext(); if (pObject->m_pIDMObject) { if (pObject->m_dwScanBits & SCAN_GC) { // Other objects may have references to this one so we need to keep this object around
// and track its references. We'll hold onto the DMObject pointer too because we may
// later need to Zombie the object in order to break a cyclic reference.
// We'll place an unloaded object with a duplicate descriptor in the cache to match the
// non-GC behavior and then move the original object into a list of released objects that
// will eventually be reclaimed by CollectGarbage.
CObject *pObjectUnloaded = new CObject(this, &pObject->m_ObjectDesc); if (!pObjectUnloaded) { return E_OUTOFMEMORY; }
if (!pObjectPrev) m_ObjectList.Remove(pObject); else pObjectPrev->Remove(pObject); m_ObjectList.AddHead(pObjectUnloaded); m_pLoader->GC_UpdateForReleasedObject(pObject); } else { pObject->m_pIDMObject->Release(); pObject->m_pIDMObject = NULL; pObject->m_ObjectDesc.m_dwValidData &= ~DMUS_OBJ_LOADED;
pObjectPrev = pObject; } } }
return S_OK; }
// return S_FALSE if the cache is already enabled according to fEnable,
// indicating it's already been done.
HRESULT CClass::EnableCache(BOOL fEnable)
{ HRESULT hr = S_FALSE; if (!fEnable) { ClearCache(false); } if( m_fKeepObjects != fEnable ) { hr = S_OK; m_fKeepObjects = fEnable; } return hr; }
typedef struct ioClass { GUID guidClass; } ioClass;
HRESULT CClass::SaveToCache(IRIFFStream *pRiff)
{ if(pRiff == NULL) { return E_POINTER; }
HRESULT hr = S_OK; IStream* pIStream = NULL; MMCKINFO ck; WORD wStructSize = 0; DWORD dwBytesWritten = 0; // DWORD dwBufferSize;
ioClass oClass;
ZeroMemory(&ck, sizeof(MMCKINFO));
pIStream = pRiff->GetStream(); if( pIStream == NULL ) { // I don't think anybody should actually be calling this function
// if they don't have a stream. Currently, this is only called by
// SaveToCache file. It definitely has a stream when it calls
// AllocRIFFStream and the stream should still be there when
// we arrive here.
assert(false);
return DMUS_E_LOADER_NOFILENAME; }
// Write class chunk header
ck.ckid = FOURCC_CLASSHEADER; if( pRiff->CreateChunk( &ck, 0 ) == S_OK ) { wStructSize = sizeof(ioClass); hr = pIStream->Write( &wStructSize, sizeof(wStructSize), &dwBytesWritten ); if( FAILED( hr ) || dwBytesWritten != sizeof(wStructSize) ) { pIStream->Release(); return DMUS_E_CANNOTWRITE; } // Prepare ioClass structure
// memset( &oClass, 0, sizeof(ioClass) );
memcpy( &oClass.guidClass, &m_ClassDesc.m_guidClass, sizeof(GUID) );
// Write Class header data
hr = pIStream->Write( &oClass, sizeof(oClass), &dwBytesWritten); if( FAILED( hr ) || dwBytesWritten != sizeof(oClass) ) { hr = DMUS_E_CANNOTWRITE; } else { if( pRiff->Ascend( &ck, 0 ) != S_OK ) { hr = DMUS_E_CANNOTSEEK; } }
} else { hr = DMUS_E_CANNOTSEEK; } pIStream->Release(); return hr; }
void CClass::PreScan()
/* Prior to scanning a directory, mark all currently loaded objects
so they won't be confused with objects loaded in the scan or referenced by the cache file. */
{ CObject *pObject = m_ObjectList.GetHead(); for (;pObject != NULL; pObject = pObject->GetNext()) { // clear the lower fields and set SCAN_PRIOR
pObject->m_dwScanBits &= ~(SCAN_CACHE | SCAN_PARSED | SCAN_SEARCH); pObject->m_dwScanBits |= SCAN_PRIOR; } }
// Helper method used to implement RemoveAndDuplicateInParentList.
void CClass::GC_Replace(CObject *pObject, CObject *pObjectReplacement)
{ m_ObjectList.Remove(pObject); if (pObjectReplacement) { m_ObjectList.AddHead(pObjectReplacement); } }
HRESULT CClass::SearchDirectory(WCHAR *pwzExtension)
{ HRESULT hr; IDirectMusicObject *pIObject; hr = CoCreateInstance(m_ClassDesc.m_guidClass, NULL,CLSCTX_INPROC_SERVER,IID_IDirectMusicObject, (void **) &pIObject); if (SUCCEEDED(hr)) { CFileStream *pStream = new CFileStream ( m_pLoader ); if (pStream) { // We need the double the MAX_PATH size since we'll be catenating strings of MAX_PATH
const int nBufferSize = 2 * MAX_PATH; WCHAR wzPath[nBufferSize]; memset(wzPath, 0, sizeof(WCHAR) * nBufferSize); hr = GetPath(wzPath); if (SUCCEEDED(hr)) { hr = S_FALSE; CObjectList TempList; #ifndef UNDER_CE
char szPath[nBufferSize]; WIN32_FIND_DATAA fileinfoA; #endif
WIN32_FIND_DATAW fileinfoW; HANDLE hFindFile; CObject * pObject; // GetPath copies at most MAX_PATH number of chars to wzPath
// This means that we have enough space to do a cat safely
WCHAR wszWildCard[3] = L"*."; wcsncat(wzPath, wszWildCard, wcslen(wszWildCard)); if (pwzExtension) { // Make sure there's enough space left in wzPath to cat pwzExtension
size_t cPathLen = wcslen(wzPath); size_t cExtLen = wcslen(pwzExtension);
// Do we have enough space to write the extension + the NULL char?
if((nBufferSize - cPathLen - 1) > cExtLen) { wcsncat(wzPath, pwzExtension, nBufferSize - wcslen(pwzExtension) - 1); } } #ifndef UNDER_CE
if (g_fIsUnicode) #endif
{ hFindFile = FindFirstFileW( wzPath, &fileinfoW ); } #ifndef UNDER_CE
else { wcstombs( szPath, wzPath, nBufferSize ); hFindFile = FindFirstFileA( szPath, &fileinfoA ); } #endif
if( hFindFile == INVALID_HANDLE_VALUE ) { pStream->Release(); pIObject->Release(); return S_FALSE; } ClearObjects(TRUE, pwzExtension); // Clear everything but the objects currently loaded.
for (;;) { BOOL fGoParse = FALSE; CDescriptor Desc; GetPath(wzPath); #ifndef UNDER_CE
if (g_fIsUnicode) #endif
{ Desc.m_ftDate = fileinfoW.ftLastWriteTime; wcsncat(wzPath, fileinfoW.cFileName, DMUS_MAX_FILENAME); } #ifndef UNDER_CE
else { Desc.m_ftDate = fileinfoA.ftLastWriteTime; WCHAR wzFileName[MAX_PATH]; mbstowcs( wzFileName, fileinfoA.cFileName, MAX_PATH ); wcsncat(wzPath, wzFileName, DMUS_MAX_FILENAME); } #endif
HRESULT hrTemp = Desc.SetFileName(wzPath); if (SUCCEEDED(hrTemp)) { Desc.m_dwValidData = (DMUS_OBJ_DATE | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH); } else { // If we couldn't set the file name, we probably don't want to continue
hr = hrTemp; break; } if (SUCCEEDED(FindObject(&Desc,&pObject))) // Make sure we don't already have it.
{ #ifndef UNDER_CE
if (g_fIsUnicode) #endif
{ fGoParse = (fileinfoW.nFileSizeLow != pObject->m_ObjectDesc.m_dwFileSize); if (!fGoParse) { fGoParse = !memcmp(&fileinfoW.ftLastWriteTime,&pObject->m_ObjectDesc.m_ftDate,sizeof(FILETIME)); } } #ifndef UNDER_CE
else { fGoParse = (fileinfoA.nFileSizeLow != pObject->m_ObjectDesc.m_dwFileSize); if (!fGoParse) { fGoParse = !memcmp(&fileinfoA.ftLastWriteTime,&pObject->m_ObjectDesc.m_ftDate,sizeof(FILETIME)); } } #endif
// Yet, disregard if it is already loaded.
if (pObject->m_pIDMObject) fGoParse = FALSE; } else fGoParse = TRUE; if (fGoParse) { hrTemp = pStream->Open(Desc.m_pwzFileName,GENERIC_READ); if (SUCCEEDED(hrTemp)) { DMUS_OBJECTDESC DESC; memset((void *)&DESC,0,sizeof(DESC)); DESC.dwSize = sizeof (DMUS_OBJECTDESC); hrTemp = pIObject->ParseDescriptor(pStream,&DESC); if (SUCCEEDED(hrTemp)) { hr = S_OK; CDescriptor ParseDesc; ParseDesc.Set(&DESC); Desc.Merge(&ParseDesc); #ifndef UNDER_CE
if (g_fIsUnicode) #endif
{ Desc.m_dwFileSize = fileinfoW.nFileSizeLow; Desc.m_ftDate = fileinfoW.ftLastWriteTime; } #ifndef UNDER_CE
else { Desc.m_dwFileSize = fileinfoA.nFileSizeLow; Desc.m_ftDate = fileinfoA.ftLastWriteTime; } #endif
if (pObject) { pObject->m_ObjectDesc.Copy(&Desc); pObject->m_dwScanBits |= SCAN_PARSED | SCAN_SEARCH; } else { pObject = new CObject(this, &Desc); if (pObject) { TempList.AddHead(pObject); pObject->m_dwScanBits |= SCAN_PARSED | SCAN_SEARCH; } } } pStream->Close(); } } #ifndef UNDER_CE
if (g_fIsUnicode) #endif
{ if ( !FindNextFileW( hFindFile, &fileinfoW ) ) break; } #ifndef UNDER_CE
else { if ( !FindNextFileA( hFindFile, &fileinfoA ) ) break; } #endif
} FindClose(hFindFile ); while (!TempList.IsEmpty()) { pObject = TempList.RemoveHead(); m_ObjectList.AddHead(pObject); } m_fDirSearched = TRUE; } pStream->Release(); } pIObject->Release(); } return hr; }
|