| /* | |
| * IDirectMusicLoaderImpl | |
| * | |
| * Copyright (C) 2003-2004 Rok Mandeljc | |
| * | |
| * This program is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this program; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include "dmloader_private.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(dmloader); | |
| static const GUID *classes[] = { | |
| &GUID_DirectMusicAllTypes, /* Keep as first */ | |
| &CLSID_DirectMusicAudioPathConfig, | |
| &CLSID_DirectMusicBand, | |
| &CLSID_DirectMusicContainer, | |
| &CLSID_DirectMusicCollection, | |
| &CLSID_DirectMusicChordMap, | |
| &CLSID_DirectMusicSegment, | |
| &CLSID_DirectMusicScript, | |
| &CLSID_DirectMusicSong, | |
| &CLSID_DirectMusicStyle, | |
| &CLSID_DirectMusicGraph, | |
| &CLSID_DirectSoundWave | |
| }; | |
| /* cache/alias entry */ | |
| struct cache_entry { | |
| struct list entry; | |
| DMUS_OBJECTDESC Desc; | |
| IDirectMusicObject *pObject; | |
| BOOL bInvalidDefaultDLS; /* workaround for enabling caching of "faulty" default dls collection */ | |
| }; | |
| typedef struct IDirectMusicLoaderImpl { | |
| IDirectMusicLoader8 IDirectMusicLoader8_iface; | |
| LONG ref; | |
| WCHAR *search_paths[ARRAY_SIZE(classes)]; | |
| unsigned int cache_class; | |
| struct list cache; | |
| } IDirectMusicLoaderImpl; | |
| static inline IDirectMusicLoaderImpl* impl_from_IDirectMusicLoader8(IDirectMusicLoader8 *iface) | |
| { | |
| return CONTAINING_RECORD(iface, IDirectMusicLoaderImpl, IDirectMusicLoader8_iface); | |
| } | |
| static int index_from_class(REFCLSID class) | |
| { | |
| int i; | |
| for (i = 0; i < ARRAY_SIZE(classes); i++) | |
| if (IsEqualGUID(class, classes[i])) | |
| return i; | |
| return -1; | |
| } | |
| static inline BOOL is_cache_enabled(IDirectMusicLoaderImpl *This, REFCLSID class) | |
| { | |
| return !!(This->cache_class & 1 << index_from_class(class)); | |
| } | |
| static void get_search_path(IDirectMusicLoaderImpl *This, REFGUID class, WCHAR *path) | |
| { | |
| int index = index_from_class(class); | |
| if (index < 0 || !This->search_paths[index]) | |
| index = 0; /* Default to GUID_DirectMusicAllTypes */ | |
| if (This->search_paths[index]) | |
| lstrcpynW(path, This->search_paths[index], MAX_PATH); | |
| else | |
| path[0] = 0; | |
| } | |
| static HRESULT DMUSIC_CopyDescriptor(DMUS_OBJECTDESC *pDst, DMUS_OBJECTDESC *pSrc) | |
| { | |
| if (TRACE_ON(dmloader)) | |
| dump_DMUS_OBJECTDESC(pSrc); | |
| /* copy field by field */ | |
| if (pSrc->dwValidData & DMUS_OBJ_CLASS) pDst->guidClass = pSrc->guidClass; | |
| if (pSrc->dwValidData & DMUS_OBJ_OBJECT) pDst->guidObject = pSrc->guidObject; | |
| if (pSrc->dwValidData & DMUS_OBJ_DATE) pDst->ftDate = pSrc->ftDate; | |
| if (pSrc->dwValidData & DMUS_OBJ_VERSION) pDst->vVersion = pSrc->vVersion; | |
| if (pSrc->dwValidData & DMUS_OBJ_NAME) strcpyW (pDst->wszName, pSrc->wszName); | |
| if (pSrc->dwValidData & DMUS_OBJ_CATEGORY) strcpyW (pDst->wszCategory, pSrc->wszCategory); | |
| if (pSrc->dwValidData & DMUS_OBJ_FILENAME) strcpyW (pDst->wszFileName, pSrc->wszFileName); | |
| if (pSrc->dwValidData & DMUS_OBJ_STREAM) IStream_Clone (pSrc->pStream, &pDst->pStream); | |
| if (pSrc->dwValidData & DMUS_OBJ_MEMORY) { | |
| pDst->pbMemData = pSrc->pbMemData; | |
| pDst->llMemLength = pSrc->llMemLength; | |
| } | |
| /* set flags */ | |
| pDst->dwValidData |= pSrc->dwValidData; | |
| return S_OK; | |
| } | |
| /***************************************************************************** | |
| * IDirectMusicLoaderImpl implementation | |
| */ | |
| /* IUnknown/IDirectMusicLoader(8) part: */ | |
| static HRESULT WINAPI IDirectMusicLoaderImpl_QueryInterface(IDirectMusicLoader8 *iface, REFIID riid, void **ppobj) | |
| { | |
| IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); | |
| TRACE("(%p, %s, %p)\n",This, debugstr_dmguid(riid), ppobj); | |
| if (IsEqualIID (riid, &IID_IUnknown) || | |
| IsEqualIID (riid, &IID_IDirectMusicLoader) || | |
| IsEqualIID (riid, &IID_IDirectMusicLoader8)) { | |
| IDirectMusicLoader_AddRef (iface); | |
| *ppobj = This; | |
| return S_OK; | |
| } | |
| WARN(": not found\n"); | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI IDirectMusicLoaderImpl_AddRef(IDirectMusicLoader8 *iface) | |
| { | |
| IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(): new ref = %u\n", iface, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI IDirectMusicLoaderImpl_Release(IDirectMusicLoader8 *iface) | |
| { | |
| IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(): new ref = %u\n", iface, ref); | |
| if (!ref) { | |
| unsigned int i; | |
| IDirectMusicLoader8_ClearCache(iface, &GUID_DirectMusicAllTypes); | |
| for (i = 0; i < ARRAY_SIZE(classes); i++) | |
| HeapFree(GetProcessHeap(), 0, This->search_paths[i]); | |
| HeapFree(GetProcessHeap(), 0, This); | |
| unlock_module(); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI IDirectMusicLoaderImpl_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc, REFIID riid, void **ppv) | |
| { | |
| IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); | |
| HRESULT result = S_OK; | |
| HRESULT ret = S_OK; /* used at the end of function, to determine whether everything went OK */ | |
| struct cache_entry *pExistingEntry, *pObjectEntry = NULL; | |
| LPSTREAM pStream; | |
| IPersistStream* pPersistStream = NULL; | |
| LPDIRECTMUSICOBJECT pObject; | |
| DMUS_OBJECTDESC GotDesc; | |
| BOOL bCache; | |
| TRACE("(%p)->(%p, %s, %p)\n", This, pDesc, debugstr_dmguid(riid), ppv); | |
| if (TRACE_ON(dmloader)) | |
| dump_DMUS_OBJECTDESC(pDesc); | |
| /* sometimes it happens that guidClass is missing... which is a BadThingTM */ | |
| if (!(pDesc->dwValidData & DMUS_OBJ_CLASS)) { | |
| ERR(": guidClass not valid but needed\n"); | |
| *ppv = NULL; | |
| return DMUS_E_LOADER_NOCLASSID; | |
| } | |
| /* OK, first we iterate through the list of objects we know about; these are either loaded (GetObject, LoadObjectFromFile) | |
| or set via SetObject; */ | |
| TRACE(": looking if we have object in the cache or if it can be found via alias\n"); | |
| LIST_FOR_EACH_ENTRY(pExistingEntry, &This->cache, struct cache_entry, entry) { | |
| if ((pDesc->dwValidData & DMUS_OBJ_OBJECT) && | |
| (pExistingEntry->Desc.dwValidData & DMUS_OBJ_OBJECT) && | |
| IsEqualGUID (&pDesc->guidObject, &pExistingEntry->Desc.guidObject)) { | |
| TRACE(": found it by object GUID\n"); | |
| /* I suppose such stuff can happen only when GUID for object is given (GUID_DefaultGMCollection) */ | |
| if (pExistingEntry->bInvalidDefaultDLS) { | |
| TRACE(": found faulty default DLS collection... enabling M$ compliant behaviour\n"); | |
| return DMUS_E_LOADER_NOFILENAME; | |
| } | |
| if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) { | |
| TRACE(": already loaded\n"); | |
| return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv); | |
| } else { | |
| TRACE(": not loaded yet\n"); | |
| pObjectEntry = pExistingEntry; | |
| } | |
| } | |
| else if ((pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) && | |
| (pExistingEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) && | |
| !strncmpW (pDesc->wszFileName, pExistingEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) { | |
| TRACE(": found it by fullpath filename\n"); | |
| if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) { | |
| TRACE(": already loaded\n"); | |
| return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv); | |
| } else { | |
| TRACE(": not loaded yet\n"); | |
| pObjectEntry = pExistingEntry; | |
| } | |
| } | |
| else if ((pDesc->dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) && | |
| (pExistingEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) && | |
| !strncmpW (pDesc->wszName, pExistingEntry->Desc.wszName, DMUS_MAX_NAME) && | |
| !strncmpW (pDesc->wszCategory, pExistingEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) { | |
| TRACE(": found it by name and category\n"); | |
| if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) { | |
| TRACE(": already loaded\n"); | |
| return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv); | |
| } else { | |
| TRACE(": not loaded yet\n"); | |
| pObjectEntry = pExistingEntry; | |
| } | |
| } | |
| else if ((pDesc->dwValidData & DMUS_OBJ_NAME) && | |
| (pExistingEntry->Desc.dwValidData & DMUS_OBJ_NAME) && | |
| !strncmpW (pDesc->wszName, pExistingEntry->Desc.wszName, DMUS_MAX_NAME)) { | |
| TRACE(": found it by name\n"); | |
| if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) { | |
| TRACE(": already loaded\n"); | |
| return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv); | |
| } else { | |
| TRACE(": not loaded yet\n"); | |
| pObjectEntry = pExistingEntry; | |
| } | |
| } | |
| else if ((pDesc->dwValidData & DMUS_OBJ_FILENAME) && | |
| (pExistingEntry->Desc.dwValidData & DMUS_OBJ_FILENAME) && | |
| !strncmpW (pDesc->wszFileName, pExistingEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) { | |
| TRACE(": found it by filename\n"); | |
| if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) { | |
| TRACE(": already loaded\n"); | |
| return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv); | |
| } else { | |
| TRACE(": not loaded yet\n"); | |
| pObjectEntry = pExistingEntry; | |
| } | |
| } | |
| } | |
| /* basically, if we found alias, we use its descriptor to load... | |
| else we use info we were given */ | |
| if (pObjectEntry) { | |
| TRACE(": found alias entry for requested object... using stored info\n"); | |
| /* I think in certain cases it can happen that entry's descriptor lacks info about | |
| where to load from (e.g.: if we loaded from stream and then released object | |
| from cache; then only its CLSID, GUID and perhaps name are left); so just | |
| overwrite whatever info the entry has (since it ought to be 100% correct) */ | |
| DMUSIC_CopyDescriptor (pDesc, &pObjectEntry->Desc); | |
| /*pDesc = &pObjectEntry->Desc; */ /* FIXME: is this OK? */ | |
| } else { | |
| TRACE(": no cache/alias entry found for requested object\n"); | |
| } | |
| if (pDesc->dwValidData & DMUS_OBJ_URL) { | |
| TRACE(": loading from URLs not supported yet\n"); | |
| return DMUS_E_LOADER_FORMATNOTSUPPORTED; | |
| } | |
| else if (pDesc->dwValidData & DMUS_OBJ_FILENAME) { | |
| /* load object from file */ | |
| /* generate filename; if it's full path, don't add search | |
| directory path, otherwise do */ | |
| WCHAR wszFileName[MAX_PATH]; | |
| if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) { | |
| lstrcpyW(wszFileName, pDesc->wszFileName); | |
| } else { | |
| WCHAR *p; | |
| get_search_path(This, &pDesc->guidClass, wszFileName); | |
| p = wszFileName + lstrlenW(wszFileName); | |
| if (p > wszFileName && p[-1] != '\\') *p++ = '\\'; | |
| strcpyW(p, pDesc->wszFileName); | |
| } | |
| TRACE(": loading from file (%s)\n", debugstr_w(wszFileName)); | |
| /* create stream and associate it with file */ | |
| result = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream); | |
| if (FAILED(result)) { | |
| ERR(": could not create file stream\n"); | |
| return result; | |
| } | |
| result = IDirectMusicLoaderFileStream_Attach (pStream, wszFileName, iface); | |
| if (FAILED(result)) { | |
| ERR(": could not attach stream to file\n"); | |
| IStream_Release (pStream); | |
| return result; | |
| } | |
| } | |
| else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) { | |
| /* load object from resource */ | |
| TRACE(": loading from resource\n"); | |
| /* create stream and associate it with given resource */ | |
| result = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pStream); | |
| if (FAILED(result)) { | |
| ERR(": could not create resource stream\n"); | |
| return result; | |
| } | |
| result = IDirectMusicLoaderResourceStream_Attach (pStream, pDesc->pbMemData, pDesc->llMemLength, 0, iface); | |
| if (FAILED(result)) { | |
| ERR(": could not attach stream to resource\n"); | |
| IStream_Release (pStream); | |
| return result; | |
| } | |
| } | |
| else if (pDesc->dwValidData & DMUS_OBJ_STREAM) { | |
| /* load object from stream */ | |
| TRACE(": loading from stream\n"); | |
| /* create universal stream and associate it with given one */ | |
| result = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pStream); | |
| if (FAILED(result)) { | |
| ERR(": could not create generic stream\n"); | |
| return result; | |
| } | |
| result = IDirectMusicLoaderGenericStream_Attach (pStream, pDesc->pStream, iface); | |
| if (FAILED(result)) { | |
| ERR(": failed to attach stream\n"); | |
| IStream_Release (pStream); | |
| return result; | |
| } | |
| } else { | |
| /* nowhere to load from */ | |
| FIXME(": unknown/unsupported way of loading\n"); | |
| return DMUS_E_LOADER_NOFILENAME; /* test shows this is returned */ | |
| } | |
| /* create object */ | |
| result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject); | |
| if (FAILED(result)) { | |
| ERR(": could not create object\n"); | |
| return result; | |
| } | |
| /* acquire PersistStream interface */ | |
| result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*)&pPersistStream); | |
| if (FAILED(result)) { | |
| ERR("failed to Query\n"); | |
| return result; | |
| } | |
| /* load */ | |
| result = IPersistStream_Load (pPersistStream, pStream); | |
| if (result != S_OK) { | |
| WARN(": failed to (completely) load object (%s)\n", debugstr_dmreturn(result)); | |
| return result; | |
| } | |
| /* get descriptor */ | |
| DM_STRUCT_INIT(&GotDesc); | |
| result = IDirectMusicObject_GetDescriptor (pObject, &GotDesc); | |
| /* set filename (if we loaded via filename) */ | |
| if (pDesc->dwValidData & DMUS_OBJ_FILENAME) { | |
| GotDesc.dwValidData |= (pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)); | |
| strcpyW (GotDesc.wszFileName, pDesc->wszFileName); | |
| } | |
| if (FAILED(result)) { | |
| ERR(": failed to get descriptor\n"); | |
| return result; | |
| } | |
| /* release all loading related stuff */ | |
| IStream_Release (pStream); | |
| IPersistStream_Release (pPersistStream); | |
| /* add object to cache/overwrite existing info (if cache is enabled) */ | |
| bCache = is_cache_enabled(This, &pDesc->guidClass); | |
| if (bCache) { | |
| if (!pObjectEntry) { | |
| pObjectEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(*pObjectEntry)); | |
| DM_STRUCT_INIT(&pObjectEntry->Desc); | |
| DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc); | |
| pObjectEntry->pObject = pObject; | |
| pObjectEntry->bInvalidDefaultDLS = FALSE; | |
| list_add_head(&This->cache, &pObjectEntry->entry); | |
| } else { | |
| DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc); | |
| pObjectEntry->pObject = pObject; | |
| pObjectEntry->bInvalidDefaultDLS = FALSE; | |
| } | |
| TRACE(": filled in cache entry\n"); | |
| } else TRACE(": caching disabled\n"); | |
| result = IDirectMusicObject_QueryInterface (pObject, riid, ppv); | |
| if (!bCache) IDirectMusicObject_Release (pObject); /* since loader's reference is not needed */ | |
| /* if there was trouble with loading, and if no other error occurred, | |
| we should return DMUS_S_PARTIALLOAD; else, error is returned */ | |
| if (result == S_OK) | |
| return ret; | |
| else | |
| return result; | |
| } | |
| static HRESULT WINAPI IDirectMusicLoaderImpl_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc) | |
| { | |
| IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); | |
| LPSTREAM pStream; | |
| LPDIRECTMUSICOBJECT pObject; | |
| DMUS_OBJECTDESC Desc; | |
| struct cache_entry *pObjectEntry, *pNewEntry; | |
| HRESULT hr; | |
| TRACE("(%p)->(%p)\n", This, pDesc); | |
| if (TRACE_ON(dmloader)) | |
| dump_DMUS_OBJECTDESC(pDesc); | |
| /* create stream and load additional info from it */ | |
| if (pDesc->dwValidData & DMUS_OBJ_FILENAME) { | |
| /* generate filename; if it's full path, don't add search | |
| directory path, otherwise do */ | |
| WCHAR wszFileName[MAX_PATH]; | |
| if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) { | |
| lstrcpyW(wszFileName, pDesc->wszFileName); | |
| } else { | |
| WCHAR *p; | |
| get_search_path(This, &pDesc->guidClass, wszFileName); | |
| p = wszFileName + lstrlenW(wszFileName); | |
| if (p > wszFileName && p[-1] != '\\') *p++ = '\\'; | |
| strcpyW(p, pDesc->wszFileName); | |
| } | |
| /* create stream */ | |
| hr = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream); | |
| if (FAILED(hr)) { | |
| ERR(": could not create file stream\n"); | |
| return DMUS_E_LOADER_FAILEDOPEN; | |
| } | |
| /* attach stream */ | |
| hr = IDirectMusicLoaderFileStream_Attach (pStream, wszFileName, iface); | |
| if (FAILED(hr)) { | |
| ERR(": could not attach stream to file %s, make sure it exists\n", debugstr_w(wszFileName)); | |
| IStream_Release (pStream); | |
| return DMUS_E_LOADER_FAILEDOPEN; | |
| } | |
| } | |
| else if (pDesc->dwValidData & DMUS_OBJ_STREAM) { | |
| /* create stream */ | |
| hr = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pStream); | |
| if (FAILED(hr)) { | |
| ERR(": could not create generic stream\n"); | |
| return DMUS_E_LOADER_FAILEDOPEN; | |
| } | |
| /* attach stream */ | |
| hr = IDirectMusicLoaderGenericStream_Attach (pStream, pDesc->pStream, iface); | |
| if (FAILED(hr)) { | |
| ERR(": could not attach stream\n"); | |
| IStream_Release (pStream); | |
| return DMUS_E_LOADER_FAILEDOPEN; | |
| } | |
| } | |
| else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) { | |
| /* create stream */ | |
| hr = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pStream); | |
| if (FAILED(hr)) { | |
| ERR(": could not create resource stream\n"); | |
| return DMUS_E_LOADER_FAILEDOPEN; | |
| } | |
| /* attach stream */ | |
| hr = IDirectMusicLoaderResourceStream_Attach (pStream, pDesc->pbMemData, pDesc->llMemLength, 0, iface); | |
| if (FAILED(hr)) { | |
| ERR(": could not attach stream to resource\n"); | |
| IStream_Release (pStream); | |
| return DMUS_E_LOADER_FAILEDOPEN; | |
| } | |
| } | |
| else { | |
| ERR(": no way to get additional info\n"); | |
| return DMUS_E_LOADER_FAILEDOPEN; | |
| } | |
| /* create object */ | |
| hr = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject); | |
| if (FAILED(hr)) { | |
| ERR("Object creation of %s failed 0x%08x\n", debugstr_guid(&pDesc->guidClass),hr); | |
| return DMUS_E_LOADER_FAILEDOPEN; | |
| } | |
| /* *sigh*... some ms objects have lousy implementation of ParseDescriptor that clears input descriptor :( */ | |
| #ifdef NOW_WE_ARE_FREE | |
| /* parse descriptor: we actually use input descriptor; fields that aren't available from stream remain, | |
| otherwise real info is set */ | |
| IDirectMusicObject_ParseDescriptor (pObject, pStream, pDesc); | |
| #endif | |
| /* hmph... due to some trouble I had with certain tests, we store current position and then set it back */ | |
| DM_STRUCT_INIT(&Desc); | |
| if (FAILED(IDirectMusicObject_ParseDescriptor (pObject, pStream, &Desc))) { | |
| ERR(": couldn't parse descriptor\n"); | |
| return DMUS_E_LOADER_FORMATNOTSUPPORTED; | |
| } | |
| /* copy elements from parsed descriptor into input descriptor; this sets new info, overwriting if necessary, | |
| but leaves info that's provided by input and not available from stream */ | |
| DMUSIC_CopyDescriptor (pDesc, &Desc); | |
| /* release everything */ | |
| IDirectMusicObject_Release (pObject); | |
| IStream_Release (pStream); | |
| /* sometimes it happens that twisted programs call SetObject for same object twice... | |
| in such cases, native loader returns S_OK and does nothing... a sound plan */ | |
| LIST_FOR_EACH_ENTRY(pObjectEntry, &This->cache, struct cache_entry, entry) { | |
| if (!memcmp (&pObjectEntry->Desc, pDesc, sizeof(DMUS_OBJECTDESC))) { | |
| TRACE(": exactly same entry already exists\n"); | |
| return S_OK; | |
| } | |
| } | |
| /* add new entry */ | |
| TRACE(": adding alias entry with following info:\n"); | |
| if (TRACE_ON(dmloader)) | |
| dump_DMUS_OBJECTDESC(pDesc); | |
| pNewEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(*pNewEntry)); | |
| /* use this function instead of pure memcpy due to streams (memcpy just copies pointer), | |
| which is basically used further by app that called SetDescriptor... better safety than exception */ | |
| DMUSIC_CopyDescriptor (&pNewEntry->Desc, pDesc); | |
| list_add_head(&This->cache, &pNewEntry->entry); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI IDirectMusicLoaderImpl_SetSearchDirectory(IDirectMusicLoader8 *iface, | |
| REFGUID class, WCHAR *path, BOOL clear) | |
| { | |
| IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); | |
| int index = index_from_class(class); | |
| DWORD attr; | |
| TRACE("(%p, %s, %s, %d)\n", This, debugstr_dmguid(class), debugstr_w(path), clear); | |
| if (!path) | |
| return E_POINTER; | |
| if (path[0]) { | |
| attr = GetFileAttributesW(path); | |
| if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY)) | |
| return DMUS_E_LOADER_BADPATH; | |
| } | |
| if (clear) | |
| FIXME("clear flag ignored\n"); | |
| /* Ignore invalid GUIDs */ | |
| if (index < 0) | |
| return S_OK; | |
| if (!This->search_paths[index]) | |
| This->search_paths[index] = HeapAlloc(GetProcessHeap(), 0, MAX_PATH); | |
| else if (!strncmpW(This->search_paths[index], path, MAX_PATH)) | |
| return S_FALSE; | |
| lstrcpynW(This->search_paths[index], path, MAX_PATH); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI IDirectMusicLoaderImpl_ScanDirectory(IDirectMusicLoader8 *iface, REFGUID rguidClass, WCHAR *pwzFileExtension, WCHAR *pwzScanFileName) | |
| { | |
| IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); | |
| static const WCHAR wszAny[] = {'*',0}; | |
| WIN32_FIND_DATAW FileData; | |
| HANDLE hSearch; | |
| WCHAR wszSearchString[MAX_PATH]; | |
| WCHAR *p; | |
| HRESULT result; | |
| TRACE("(%p, %s, %s, %s)\n", This, debugstr_dmguid(rguidClass), debugstr_w(pwzFileExtension), | |
| debugstr_w(pwzScanFileName)); | |
| if (index_from_class(rguidClass) <= 0) { | |
| ERR(": rguidClass invalid CLSID\n"); | |
| return REGDB_E_CLASSNOTREG; | |
| } | |
| if (!pwzFileExtension) | |
| return S_FALSE; | |
| /* get search path for given class */ | |
| get_search_path(This, rguidClass, wszSearchString); | |
| p = wszSearchString + lstrlenW(wszSearchString); | |
| if (p > wszSearchString && p[-1] != '\\') *p++ = '\\'; | |
| *p++ = '*'; /* any file */ | |
| if (strcmpW (pwzFileExtension, wszAny)) *p++ = '.'; /* if we have actual extension, put a dot */ | |
| strcpyW (p, pwzFileExtension); | |
| TRACE(": search string: %s\n", debugstr_w(wszSearchString)); | |
| hSearch = FindFirstFileW (wszSearchString, &FileData); | |
| if (hSearch == INVALID_HANDLE_VALUE) { | |
| TRACE(": no files found\n"); | |
| return S_FALSE; | |
| } | |
| do { | |
| DMUS_OBJECTDESC Desc; | |
| DM_STRUCT_INIT(&Desc); | |
| Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_DATE; | |
| Desc.guidClass = *rguidClass; | |
| strcpyW (Desc.wszFileName, FileData.cFileName); | |
| FileTimeToLocalFileTime (&FileData.ftCreationTime, &Desc.ftDate); | |
| IDirectMusicLoader8_SetObject (iface, &Desc); | |
| if (!FindNextFileW (hSearch, &FileData)) { | |
| if (GetLastError () == ERROR_NO_MORE_FILES) { | |
| TRACE(": search completed\n"); | |
| result = S_OK; | |
| } else { | |
| ERR(": could not get next file\n"); | |
| result = E_FAIL; | |
| } | |
| FindClose (hSearch); | |
| return result; | |
| } | |
| } while (1); | |
| } | |
| static HRESULT WINAPI IDirectMusicLoaderImpl_CacheObject(IDirectMusicLoader8 *iface, IDirectMusicObject *pObject) | |
| { | |
| IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); | |
| DMUS_OBJECTDESC Desc; | |
| HRESULT result = DMUS_E_LOADER_OBJECTNOTFOUND; | |
| struct cache_entry *pObjectEntry; | |
| TRACE("(%p, %p)\n", This, pObject); | |
| /* get descriptor */ | |
| DM_STRUCT_INIT(&Desc); | |
| IDirectMusicObject_GetDescriptor (pObject, &Desc); | |
| /* now iterate through the list and check if we have an alias (without object), corresponding | |
| to the descriptor of the input object */ | |
| LIST_FOR_EACH_ENTRY(pObjectEntry, &This->cache, struct cache_entry, entry) { | |
| if ((Desc.dwValidData & DMUS_OBJ_OBJECT) && | |
| (pObjectEntry->Desc.dwValidData & DMUS_OBJ_OBJECT) && | |
| IsEqualGUID (&Desc.guidObject, &pObjectEntry->Desc.guidObject)) { | |
| TRACE(": found it by object GUID\n"); | |
| if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject) | |
| result = S_FALSE; | |
| else | |
| result = S_OK; | |
| break; | |
| } | |
| else if ((Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) && | |
| (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) && | |
| !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) { | |
| TRACE(": found it by fullpath filename\n"); | |
| if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject) | |
| result = S_FALSE; | |
| else | |
| result = S_OK; | |
| break; | |
| } | |
| else if ((Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) && | |
| (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) && | |
| !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME) && | |
| !strncmpW (Desc.wszCategory, pObjectEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) { | |
| TRACE(": found it by name and category\n"); | |
| if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject) | |
| result = S_FALSE; | |
| else | |
| result = S_OK; | |
| break; | |
| } | |
| else if ((Desc.dwValidData & DMUS_OBJ_NAME) && | |
| (pObjectEntry->Desc.dwValidData & DMUS_OBJ_NAME) && | |
| !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME)) { | |
| TRACE(": found it by name\n"); | |
| if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject) | |
| result = S_FALSE; | |
| else | |
| result = S_OK; | |
| break; | |
| } | |
| else if ((Desc.dwValidData & DMUS_OBJ_FILENAME) && | |
| (pObjectEntry->Desc.dwValidData & DMUS_OBJ_FILENAME) && | |
| !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) { | |
| TRACE(": found it by filename\n"); | |
| if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject) | |
| result = S_FALSE; | |
| else | |
| result = S_OK; | |
| break; | |
| } | |
| } | |
| /* if we found such alias, then set everything */ | |
| if (result == S_OK) { | |
| pObjectEntry->Desc.dwValidData &= DMUS_OBJ_LOADED; | |
| pObjectEntry->pObject = pObject; | |
| IDirectMusicObject_AddRef (pObjectEntry->pObject); | |
| } | |
| return result; | |
| } | |
| static HRESULT WINAPI IDirectMusicLoaderImpl_ReleaseObject(IDirectMusicLoader8 *iface, IDirectMusicObject *pObject) | |
| { | |
| IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); | |
| DMUS_OBJECTDESC Desc; | |
| struct cache_entry *pObjectEntry; | |
| HRESULT result = S_FALSE; | |
| TRACE("(%p, %p)\n", This, pObject); | |
| if(!pObject) return E_POINTER; | |
| /* get descriptor */ | |
| DM_STRUCT_INIT(&Desc); | |
| IDirectMusicObject_GetDescriptor (pObject, &Desc); | |
| /* iterate through the list of objects we know about; check only those with DMUS_OBJ_LOADED */ | |
| TRACE(": looking for the object in cache\n"); | |
| LIST_FOR_EACH_ENTRY(pObjectEntry, &This->cache, struct cache_entry, entry) { | |
| if ((Desc.dwValidData & DMUS_OBJ_OBJECT) && | |
| (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_OBJECT | DMUS_OBJ_LOADED)) && | |
| IsEqualGUID (&Desc.guidObject, &pObjectEntry->Desc.guidObject)) { | |
| TRACE(": found it by object GUID\n"); | |
| if (TRACE_ON(dmloader)) | |
| dump_DMUS_OBJECTDESC(&pObjectEntry->Desc); | |
| result = S_OK; | |
| break; | |
| } | |
| else if ((Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) && | |
| (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_LOADED)) && | |
| !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) { | |
| TRACE(": found it by fullpath filename\n"); | |
| result = S_OK; | |
| break; | |
| } | |
| else if ((Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) && | |
| (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY | DMUS_OBJ_LOADED)) && | |
| !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME) && | |
| !strncmpW (Desc.wszCategory, pObjectEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) { | |
| TRACE(": found it by name and category\n"); | |
| result = S_OK; | |
| break; | |
| } | |
| else if ((Desc.dwValidData & DMUS_OBJ_NAME) && | |
| (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_LOADED)) && | |
| !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME)) { | |
| TRACE(": found it by name\n"); | |
| result = S_OK; | |
| break; | |
| } | |
| else if ((Desc.dwValidData & DMUS_OBJ_FILENAME) && | |
| (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_LOADED)) && | |
| !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) { | |
| TRACE(": found it by filename\n"); | |
| result = S_OK; | |
| break; | |
| } | |
| } | |
| if (result == S_OK) { | |
| /*TRACE(": releasing:\n%s - bInvalidDefaultDLS = %i\n - pObject = %p\n", debugstr_DMUS_OBJECTDESC(&pObjectEntry->Desc), pObjectEntry->bInvalidDefaultDLS, pObjectEntry->pObject); */ | |
| IDirectMusicObject_Release (pObjectEntry->pObject); | |
| pObjectEntry->pObject = NULL; | |
| pObjectEntry->Desc.dwValidData &= ~DMUS_OBJ_LOADED; | |
| } | |
| return result; | |
| } | |
| static HRESULT WINAPI IDirectMusicLoaderImpl_ClearCache(IDirectMusicLoader8 *iface, REFGUID class) | |
| { | |
| IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); | |
| struct cache_entry *obj, *obj2; | |
| TRACE("(%p, %s)\n", This, debugstr_dmguid(class)); | |
| LIST_FOR_EACH_ENTRY_SAFE(obj, obj2, &This->cache, struct cache_entry, entry) { | |
| if ((IsEqualGUID(class, &GUID_DirectMusicAllTypes) || IsEqualGUID(class, &obj->Desc.guidClass)) && | |
| (obj->Desc.dwValidData & DMUS_OBJ_LOADED)) { | |
| /* basically, wrap to ReleaseObject for each object found */ | |
| IDirectMusicLoader8_ReleaseObject(iface, obj->pObject); | |
| list_remove(&obj->entry); | |
| HeapFree(GetProcessHeap(), 0, obj); | |
| } | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI IDirectMusicLoaderImpl_EnableCache(IDirectMusicLoader8 *iface, REFGUID class, | |
| BOOL enable) | |
| { | |
| IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); | |
| BOOL current; | |
| TRACE("(%p, %s, %d)\n", This, debugstr_dmguid(class), enable); | |
| current = is_cache_enabled(This, class); | |
| if (IsEqualGUID(class, &GUID_DirectMusicAllTypes)) | |
| This->cache_class = enable ? ~0 : 0; | |
| else { | |
| if (enable) | |
| This->cache_class |= 1 << index_from_class(class); | |
| else | |
| This->cache_class &= ~(1 << index_from_class(class)); | |
| } | |
| if (!enable) | |
| IDirectMusicLoader8_ClearCache(iface, class); | |
| if (current == enable) | |
| return S_FALSE; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI IDirectMusicLoaderImpl_EnumObject(IDirectMusicLoader8 *iface, REFGUID rguidClass, DWORD dwIndex, DMUS_OBJECTDESC *pDesc) | |
| { | |
| IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); | |
| DWORD dwCount = 0; | |
| struct cache_entry *pObjectEntry; | |
| TRACE("(%p, %s, %d, %p)\n", This, debugstr_dmguid(rguidClass), dwIndex, pDesc); | |
| DM_STRUCT_INIT(pDesc); | |
| LIST_FOR_EACH_ENTRY(pObjectEntry, &This->cache, struct cache_entry, entry) { | |
| if (IsEqualGUID (rguidClass, &GUID_DirectMusicAllTypes) || IsEqualGUID (rguidClass, &pObjectEntry->Desc.guidClass)) { | |
| if (dwCount == dwIndex) { | |
| *pDesc = pObjectEntry->Desc; | |
| /* we aren't supposed to reveal this info */ | |
| pDesc->dwValidData &= ~(DMUS_OBJ_MEMORY | DMUS_OBJ_STREAM); | |
| pDesc->pbMemData = NULL; | |
| pDesc->llMemLength = 0; | |
| pDesc->pStream = NULL; | |
| return S_OK; | |
| } | |
| dwCount++; | |
| } | |
| } | |
| TRACE(": not found\n"); | |
| return S_FALSE; | |
| } | |
| static void WINAPI IDirectMusicLoaderImpl_CollectGarbage(IDirectMusicLoader8 *iface) | |
| { | |
| FIXME("(%p)->(): stub\n", iface); | |
| } | |
| static HRESULT WINAPI IDirectMusicLoaderImpl_ReleaseObjectByUnknown(IDirectMusicLoader8 *iface, IUnknown *pObject) | |
| { | |
| IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); | |
| HRESULT result; | |
| LPDIRECTMUSICOBJECT pObjectInterface; | |
| TRACE("(%p, %p)\n", This, pObject); | |
| if (IsBadReadPtr (pObject, sizeof(*pObject))) { | |
| ERR(": pObject bad write pointer\n"); | |
| return E_POINTER; | |
| } | |
| /* we simply get IDirectMusicObject interface */ | |
| result = IUnknown_QueryInterface (pObject, &IID_IDirectMusicObject, (LPVOID*)&pObjectInterface); | |
| if (FAILED(result)) return result; | |
| /* and release it in old-fashioned way */ | |
| result = IDirectMusicLoader8_ReleaseObject (iface, pObjectInterface); | |
| IDirectMusicObject_Release (pObjectInterface); | |
| return result; | |
| } | |
| static HRESULT WINAPI IDirectMusicLoaderImpl_LoadObjectFromFile(IDirectMusicLoader8 *iface, REFGUID rguidClassID, REFIID iidInterfaceID, WCHAR *pwzFilePath, void **ppObject) | |
| { | |
| IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface); | |
| DMUS_OBJECTDESC ObjDesc; | |
| WCHAR wszLoaderSearchPath[MAX_PATH]; | |
| TRACE("(%p, %s, %s, %s, %p): wrapping to IDirectMusicLoaderImpl_GetObject\n", This, debugstr_dmguid(rguidClassID), debugstr_dmguid(iidInterfaceID), debugstr_w(pwzFilePath), ppObject); | |
| DM_STRUCT_INIT(&ObjDesc); | |
| ObjDesc.dwValidData = DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_CLASS; /* I believe I've read somewhere in MSDN that this function requires either full path or relative path */ | |
| ObjDesc.guidClass = *rguidClassID; | |
| /* OK, MSDN says that search order is the following: | |
| - current directory (DONE) | |
| - windows search path (FIXME: how do I get that?) | |
| - loader's search path (DONE) | |
| */ | |
| get_search_path(This, rguidClassID, wszLoaderSearchPath); | |
| /* search in current directory */ | |
| if (!SearchPathW (NULL, pwzFilePath, NULL, sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL) && | |
| /* search in loader's search path */ | |
| !SearchPathW (wszLoaderSearchPath, pwzFilePath, NULL, sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL)) { | |
| /* cannot find file */ | |
| TRACE(": cannot find file\n"); | |
| return DMUS_E_LOADER_FAILEDOPEN; | |
| } | |
| TRACE(": full file path = %s\n", debugstr_w (ObjDesc.wszFileName)); | |
| return IDirectMusicLoader_GetObject(iface, &ObjDesc, iidInterfaceID, ppObject); | |
| } | |
| static const IDirectMusicLoader8Vtbl DirectMusicLoader_Loader_Vtbl = { | |
| IDirectMusicLoaderImpl_QueryInterface, | |
| IDirectMusicLoaderImpl_AddRef, | |
| IDirectMusicLoaderImpl_Release, | |
| IDirectMusicLoaderImpl_GetObject, | |
| IDirectMusicLoaderImpl_SetObject, | |
| IDirectMusicLoaderImpl_SetSearchDirectory, | |
| IDirectMusicLoaderImpl_ScanDirectory, | |
| IDirectMusicLoaderImpl_CacheObject, | |
| IDirectMusicLoaderImpl_ReleaseObject, | |
| IDirectMusicLoaderImpl_ClearCache, | |
| IDirectMusicLoaderImpl_EnableCache, | |
| IDirectMusicLoaderImpl_EnumObject, | |
| IDirectMusicLoaderImpl_CollectGarbage, | |
| IDirectMusicLoaderImpl_ReleaseObjectByUnknown, | |
| IDirectMusicLoaderImpl_LoadObjectFromFile | |
| }; | |
| /* help function for DMUSIC_SetDefaultDLS */ | |
| static HRESULT DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]) { | |
| HKEY hkDM; | |
| DWORD returnType, sizeOfReturnBuffer = MAX_PATH; | |
| char szPath[MAX_PATH]; | |
| if ((RegOpenKeyExA (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkDM) != ERROR_SUCCESS) || | |
| (RegQueryValueExA (hkDM, "GMFilePath", NULL, &returnType, (LPBYTE) szPath, &sizeOfReturnBuffer) != ERROR_SUCCESS)) { | |
| WARN(": registry entry missing\n" ); | |
| return E_FAIL; | |
| } | |
| /* FIXME: Check return types to ensure we're interpreting data right */ | |
| MultiByteToWideChar (CP_ACP, 0, szPath, -1, wszPath, MAX_PATH); | |
| return S_OK; | |
| } | |
| /* for ClassFactory */ | |
| HRESULT WINAPI create_dmloader(REFIID lpcGUID, void **ppobj) | |
| { | |
| IDirectMusicLoaderImpl *obj; | |
| DMUS_OBJECTDESC Desc; | |
| struct cache_entry *dls; | |
| struct list *pEntry; | |
| TRACE("(%s, %p)\n", debugstr_dmguid(lpcGUID), ppobj); | |
| obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoaderImpl)); | |
| if (NULL == obj) { | |
| *ppobj = NULL; | |
| return E_OUTOFMEMORY; | |
| } | |
| obj->IDirectMusicLoader8_iface.lpVtbl = &DirectMusicLoader_Loader_Vtbl; | |
| obj->ref = 0; /* Will be inited with QueryInterface */ | |
| list_init(&obj->cache); | |
| /* Caching is enabled by default for all classes */ | |
| obj->cache_class = ~0; | |
| /* set default DLS collection (via SetObject... so that loading via DMUS_OBJ_OBJECT is possible) */ | |
| DM_STRUCT_INIT(&Desc); | |
| Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_OBJECT; | |
| Desc.guidClass = CLSID_DirectMusicCollection; | |
| Desc.guidObject = GUID_DefaultGMCollection; | |
| DMUSIC_GetDefaultGMPath (Desc.wszFileName); | |
| IDirectMusicLoader_SetObject(&obj->IDirectMusicLoader8_iface, &Desc); | |
| /* and now the workaroundTM for "invalid" default DLS; basically, | |
| my tests showed that if GUID chunk is present in default DLS | |
| collection, loader treats it as "invalid" and returns | |
| DMUS_E_LOADER_NOFILENAME for all requests for it; basically, we check | |
| if out input guidObject was overwritten */ | |
| pEntry = list_head(&obj->cache); | |
| dls = LIST_ENTRY(pEntry, struct cache_entry, entry); | |
| if (!IsEqualGUID(&Desc.guidObject, &GUID_DefaultGMCollection)) { | |
| dls->bInvalidDefaultDLS = TRUE; | |
| } | |
| lock_module(); | |
| return IDirectMusicLoader_QueryInterface(&obj->IDirectMusicLoader8_iface, lpcGUID, ppobj); | |
| } |