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.
1600 lines
47 KiB
1600 lines
47 KiB
// 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;
|
|
}
|