| /* 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 HRESULT DMUSIC_InitLoaderSettings (LPDIRECTMUSICLOADER8 iface); | 
 | static HRESULT DMUSIC_GetLoaderSettings (LPDIRECTMUSICLOADER8 iface, REFGUID pClassID, WCHAR* wszSearchPath, LPBOOL pbCache); | 
 | static HRESULT DMUSIC_SetLoaderSettings (LPDIRECTMUSICLOADER8 iface, REFGUID pClassID, WCHAR* wszSearchPath, LPBOOL pbCache); | 
 |  | 
 | static HRESULT DMUSIC_CopyDescriptor (LPDMUS_OBJECTDESC pDst, LPDMUS_OBJECTDESC pSrc) { | 
 | 	TRACE(": copy\n%s\n", debugstr_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; | 
 | } | 
 |  | 
 |  | 
 | static BOOL DMUSIC_IsValidLoadableClass (REFCLSID pClassID) { | 
 | 	if (IsEqualCLSID(pClassID, &CLSID_DirectMusicAudioPathConfig) || | 
 | 		IsEqualCLSID(pClassID, &CLSID_DirectMusicBand) || | 
 | 		IsEqualCLSID(pClassID, &CLSID_DirectMusicContainer) || | 
 | 		IsEqualCLSID(pClassID, &CLSID_DirectMusicCollection) || | 
 | 		IsEqualCLSID(pClassID, &CLSID_DirectMusicChordMap) || | 
 | 		IsEqualCLSID(pClassID, &CLSID_DirectMusicSegment) || | 
 | 		IsEqualCLSID(pClassID, &CLSID_DirectMusicScript) || | 
 | 		IsEqualCLSID(pClassID, &CLSID_DirectMusicSong) || | 
 | 		IsEqualCLSID(pClassID, &CLSID_DirectMusicStyle) || | 
 | 		IsEqualCLSID(pClassID, &CLSID_DirectMusicGraph) || | 
 | 		IsEqualCLSID(pClassID, &CLSID_DirectSoundWave) || | 
 | 		IsEqualCLSID(pClassID, &GUID_DirectMusicAllTypes)) | 
 | 		return TRUE; | 
 | 	else | 
 | 		return FALSE; | 
 | } | 
 |  | 
 | /***************************************************************************** | 
 |  * IDirectMusicLoaderImpl implementation | 
 |  */ | 
 | /* IUnknown/IDirectMusicLoader(8) part: */ | 
 |  | 
 | static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_QueryInterface (LPDIRECTMUSICLOADER8 iface, REFIID riid, LPVOID *ppobj) { | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, 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_IDirectMusicLoader_AddRef (LPDIRECTMUSICLOADER8 iface) { | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	TRACE("(%p): AddRef from %d\n", This, This->dwRef); | 
 | 	return InterlockedIncrement (&This->dwRef); | 
 | } | 
 |  | 
 | static ULONG WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_Release (LPDIRECTMUSICLOADER8 iface) { | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	 | 
 | 	DWORD dwRef = InterlockedDecrement (&This->dwRef); | 
 | 	TRACE("(%p): ReleaseRef to %d\n", This, This->dwRef); | 
 | 	if (dwRef == 0) { | 
 |             /* firstly, release the cache */ | 
 |             IDirectMusicLoader8_ClearCache (iface, &GUID_DirectMusicAllTypes); | 
 |             /* FIXME: release all allocated entries */ | 
 |             /* destroy critical section */ | 
 |             /*This->CritSect.DebugInfo->Spare[0] = 0; | 
 |               DeleteCriticalSection (&This->CritSect); */ | 
 |             HeapFree (GetProcessHeap(), 0, This); | 
 |  | 
 |             /* decrease number of instances */ | 
 |             InterlockedDecrement (&dwDirectMusicLoader); | 
 | 	} | 
 | 	 | 
 | 	return dwRef; | 
 | } | 
 |  | 
 | static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_GetObject (LPDIRECTMUSICLOADER8 iface, LPDMUS_OBJECTDESC pDesc, REFIID riid, LPVOID* ppv) { | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	HRESULT result = S_OK; | 
 | 	HRESULT ret = S_OK; /* used at the end of function, to determine whether everything went OK */ | 
 | 	 | 
 | 	struct list *pEntry; | 
 | 	LPWINE_LOADER_ENTRY pObjectEntry = NULL; | 
 | 	LPSTREAM pStream; | 
 | 	IPersistStream* pPersistStream = NULL; | 
 |  | 
 | 	LPDIRECTMUSICOBJECT pObject; | 
 | 	DMUS_OBJECTDESC GotDesc; | 
 | 	BOOL bCache; | 
 |  | 
 | 	TRACE("(%p, %p, %s, %p): pDesc:\n%s\n", This, pDesc, debugstr_dmguid(riid), ppv, debugstr_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 thru 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(pEntry, This->pObjects) { | 
 | 		LPWINE_LOADER_ENTRY pExistingEntry = LIST_ENTRY(pEntry, WINE_LOADER_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, wszSearchPath[MAX_PATH]; | 
 | 			DMUSIC_GetLoaderSettings (iface, &pDesc->guidClass, wszSearchPath, NULL); | 
 | 			lstrcpyW(wszFileName, wszSearchPath); | 
 | 			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)); | 
 | 		ret = DMUS_S_PARTIALLOAD /*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) */ | 
 | 	DMUSIC_GetLoaderSettings (iface, &pDesc->guidClass, NULL, &bCache); | 
 | 	if (bCache) { | 
 | 		if (!pObjectEntry) { | 
 | 			pObjectEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(WINE_LOADER_ENTRY)); | 
 | 			DM_STRUCT_INIT(&pObjectEntry->Desc); | 
 | 			DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc); | 
 | 			pObjectEntry->pObject = pObject; | 
 | 			pObjectEntry->bInvalidDefaultDLS = FALSE; | 
 | 			list_add_head (This->pObjects, &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"); | 
 |  | 
 | #if 0 | 
 | 	/* for debug purposes (e.g. to check if all files are cached) */ | 
 | 	TRACE("*** Loader's cache ***\n"); | 
 | 	int i = 0; | 
 | 	LIST_FOR_EACH (pEntry, This->pObjects) { | 
 | 		i++; | 
 | 		pObjectEntry = LIST_ENTRY(pEntry, WINE_LOADER_ENTRY, entry); | 
 | 		TRACE(": entry nr. %i:\n%s\n  - bInvalidDefaultDLS = %i\n  - pObject = %p\n", i, debugstr_DMUS_OBJECTDESC(&pObjectEntry->Desc), pObjectEntry->bInvalidDefaultDLS, pObjectEntry->pObject); | 
 | 	} | 
 | #endif | 
 | 	 | 
 | 	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_IDirectMusicLoader_SetObject (LPDIRECTMUSICLOADER8 iface, LPDMUS_OBJECTDESC pDesc) { | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	LPSTREAM pStream; | 
 | 	LPDIRECTMUSICOBJECT pObject; | 
 | 	DMUS_OBJECTDESC Desc; | 
 | 	struct list *pEntry; | 
 | 	LPWINE_LOADER_ENTRY pObjectEntry, pNewEntry; | 
 | 	HRESULT hr; | 
 |  | 
 | 	TRACE("(%p, %p): pDesc:\n%s\n", This, pDesc, debugstr_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; | 
 | 			WCHAR wszSearchPath[MAX_PATH]; | 
 | 			DMUSIC_GetLoaderSettings (iface, &pDesc->guidClass, wszSearchPath, NULL); | 
 | 			lstrcpyW(wszFileName, wszSearchPath); | 
 | 			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\n"); | 
 | 			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 */ | 
 | 	CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject); | 
 |  | 
 | 	/* *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 (pEntry, This->pObjects) { | 
 | 		pObjectEntry = LIST_ENTRY (pEntry, WINE_LOADER_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%s\n", debugstr_DMUS_OBJECTDESC(pDesc)); | 
 | 	pNewEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(WINE_LOADER_ENTRY)); | 
 | 	/* 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->pObjects, &pNewEntry->entry); | 
 |  | 
 | 	return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_SetSearchDirectory (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, WCHAR* pwzPath, BOOL fClear) { | 
 | 	WCHAR wszCurrentPath[MAX_PATH]; | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	TRACE("(%p, %s, %s, %d)\n", This, debugstr_dmguid(rguidClass), debugstr_w(pwzPath), fClear); | 
 | 	FIXME(": fClear ignored\n"); | 
 | 	DMUSIC_GetLoaderSettings (iface, rguidClass, wszCurrentPath, NULL); | 
 | 	if (!strncmpW(wszCurrentPath, pwzPath, MAX_PATH)) { | 
 | 	  return S_FALSE; | 
 | 	} | 
 | 	/* FIXME: check if path is valid; else return DMUS_E_LOADER_BADPATH */ | 
 | 	return DMUSIC_SetLoaderSettings (iface, rguidClass, pwzPath, NULL); | 
 | } | 
 |  | 
 | static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_ScanDirectory (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, WCHAR* pwzFileExtension, WCHAR* pwzScanFileName) { | 
 | 	static const WCHAR wszAny[] = {'*',0}; | 
 | 	WIN32_FIND_DATAW FileData; | 
 | 	HANDLE hSearch; | 
 | 	WCHAR wszSearchString[MAX_PATH]; | 
 | 	WCHAR *p; | 
 | 	HRESULT result; | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	TRACE("(%p, %s, %p, %p)\n", This, debugstr_dmguid(rguidClass), pwzFileExtension, pwzScanFileName); | 
 | 	if (IsEqualGUID (rguidClass, &GUID_DirectMusicAllTypes) || !DMUSIC_IsValidLoadableClass(rguidClass)) { | 
 | 		ERR(": rguidClass invalid CLSID\n"); | 
 | 		return REGDB_E_CLASSNOTREG; | 
 | 	} | 
 | 	 | 
 | 	/* get search path for given class */ | 
 | 	DMUSIC_GetLoaderSettings (iface, rguidClass, wszSearchString, NULL); | 
 | 	 | 
 | 	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_IDirectMusicLoader_CacheObject (LPDIRECTMUSICLOADER8 iface, IDirectMusicObject* pObject) { | 
 | 	DMUS_OBJECTDESC Desc; | 
 | 	HRESULT result = DMUS_E_LOADER_OBJECTNOTFOUND; | 
 | 	struct list *pEntry; | 
 | 	LPWINE_LOADER_ENTRY  pObjectEntry = NULL; | 
 |  | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	TRACE("(%p, %p)\n", This, pObject); | 
 | 	 | 
 | 	/* get descriptor */ | 
 | 	DM_STRUCT_INIT(&Desc); | 
 | 	IDirectMusicObject_GetDescriptor (pObject, &Desc); | 
 | 	 | 
 | 	/* now iterate thru list and check if we have alias (without object), corresponding | 
 | 	   to descriptor of input object */ | 
 | 	LIST_FOR_EACH(pEntry, This->pObjects) { | 
 | 		pObjectEntry = LIST_ENTRY(pEntry, WINE_LOADER_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_IDirectMusicLoader_ReleaseObject (LPDIRECTMUSICLOADER8 iface, IDirectMusicObject* pObject) { | 
 | 	DMUS_OBJECTDESC Desc; | 
 | 	struct list *pEntry; | 
 | 	LPWINE_LOADER_ENTRY pObjectEntry = NULL; | 
 | 	HRESULT result = S_FALSE; | 
 |  | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	TRACE("(%p, %p)\n", This, pObject); | 
 | 	 | 
 | 	if(!pObject) return E_POINTER; | 
 |  | 
 | 	/* get descriptor */ | 
 | 	DM_STRUCT_INIT(&Desc); | 
 | 	IDirectMusicObject_GetDescriptor (pObject, &Desc); | 
 | 	 | 
 | 	/* iterate thru 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(pEntry, This->pObjects) { | 
 | 		pObjectEntry = LIST_ENTRY(pEntry, WINE_LOADER_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%s\n", debugstr_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_IDirectMusicLoader_ClearCache (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass) { | 
 | 	struct list *pEntry; | 
 | 	LPWINE_LOADER_ENTRY pObjectEntry; | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	TRACE("(%p, %s)\n", This, debugstr_dmguid(rguidClass)); | 
 | 	 | 
 | 	LIST_FOR_EACH (pEntry, This->pObjects) { | 
 | 		pObjectEntry = LIST_ENTRY (pEntry, WINE_LOADER_ENTRY, entry); | 
 | 		 | 
 | 		if ((IsEqualGUID (rguidClass, &GUID_DirectMusicAllTypes) || IsEqualGUID (rguidClass, &pObjectEntry->Desc.guidClass)) && | 
 | 			(pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED)) { | 
 | 			/* basically, wrap to ReleaseObject for each object found */ | 
 | 			IDirectMusicLoader8_ReleaseObject (iface, pObjectEntry->pObject); | 
 | 		} | 
 | 	} | 
 | 	 | 
 | 	return S_OK; | 
 | } | 
 |  | 
 | static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_EnableCache (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, BOOL fEnable) { | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	BOOL bCurrent; | 
 | 	TRACE("(%p, %s, %d)\n", This, debugstr_dmguid(rguidClass), fEnable); | 
 | 	DMUSIC_GetLoaderSettings (iface, rguidClass, NULL, &bCurrent); | 
 | 	if (bCurrent == fEnable) | 
 | 		return S_FALSE; | 
 | 	else | 
 | 		return DMUSIC_SetLoaderSettings (iface, rguidClass, NULL, &fEnable); | 
 | } | 
 |  | 
 | static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_EnumObject (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, DWORD dwIndex, LPDMUS_OBJECTDESC pDesc) { | 
 | 	DWORD dwCount = 0; | 
 | 	struct list *pEntry; | 
 | 	LPWINE_LOADER_ENTRY pObjectEntry; | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	TRACE("(%p, %s, %d, %p)\n", This, debugstr_dmguid(rguidClass), dwIndex, pDesc); | 
 |  | 
 | 	DM_STRUCT_INIT(pDesc); | 
 |  | 
 | 	LIST_FOR_EACH (pEntry, This->pObjects) { | 
 | 		pObjectEntry = LIST_ENTRY (pEntry, WINE_LOADER_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_IDirectMusicLoader_CollectGarbage (LPDIRECTMUSICLOADER8 iface) { | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	FIXME("(%p): stub\n", This); | 
 | } | 
 |  | 
 | static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_ReleaseObjectByUnknown (LPDIRECTMUSICLOADER8 iface, IUnknown* pObject) { | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	HRESULT result; | 
 | 	LPDIRECTMUSICOBJECT pObjectInterface; | 
 | 	 | 
 | 	TRACE("(%p, %p)\n", This, pObject); | 
 | 	 | 
 | 	if (IsBadReadPtr (pObject, sizeof(LPUNKNOWN))) { | 
 | 		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_IDirectMusicLoader_LoadObjectFromFile (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClassID, REFIID iidInterfaceID, WCHAR* pwzFilePath, void** ppObject) { | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, 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) | 
 | 	*/ | 
 | 	DMUSIC_GetLoaderSettings (iface, rguidClassID, wszLoaderSearchPath, NULL); | 
 |     /* 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 IDirectMusicLoaderImpl_IDirectMusicLoader_GetObject (iface, &ObjDesc, iidInterfaceID, ppObject); | 
 | } | 
 |  | 
 | static const IDirectMusicLoader8Vtbl DirectMusicLoader_Loader_Vtbl = { | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_QueryInterface, | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_AddRef, | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_Release, | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_GetObject, | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_SetObject, | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_SetSearchDirectory, | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_ScanDirectory, | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_CacheObject, | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_ReleaseObject, | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_ClearCache, | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_EnableCache, | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_EnumObject, | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_CollectGarbage, | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_ReleaseObjectByUnknown, | 
 | 	IDirectMusicLoaderImpl_IDirectMusicLoader_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 DMUSIC_CreateDirectMusicLoaderImpl (LPCGUID lpcGUID, LPVOID *ppobj, LPUNKNOWN pUnkOuter) { | 
 | 	IDirectMusicLoaderImpl *obj; | 
 | 	DMUS_OBJECTDESC Desc; | 
 | 	LPWINE_LOADER_ENTRY pDefaultDLSEntry; | 
 | 	struct list *pEntry; | 
 |  | 
 | 	TRACE("(%s, %p, %p)\n", debugstr_dmguid(lpcGUID), ppobj, pUnkOuter); | 
 | 	obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoaderImpl)); | 
 | 	if (NULL == obj) { | 
 | 		*ppobj = NULL; | 
 | 		return E_OUTOFMEMORY; | 
 | 	} | 
 | 	obj->LoaderVtbl = &DirectMusicLoader_Loader_Vtbl; | 
 | 	obj->dwRef = 0; /* will be inited with QueryInterface */ | 
 | 	/* init critical section */ | 
 | 	/* init cache/alias list */ | 
 | 	/*InitializeCriticalSection (&obj->CritSect); | 
 | 	obj->CritSect.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IDirectMusicLoaderImpl.CritSect"); */ | 
 | 	obj->pObjects = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(struct list)); | 
 | 	list_init (obj->pObjects); | 
 | 	/* init settings */ | 
 | 	obj->pClassSettings = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(struct list)); | 
 | 	list_init (obj->pClassSettings); | 
 | 	DMUSIC_InitLoaderSettings ((LPDIRECTMUSICLOADER8)obj); | 
 |  | 
 | 	/* 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 ((LPDIRECTMUSICLOADER8)obj, &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->pObjects); | 
 | 	pDefaultDLSEntry = LIST_ENTRY (pEntry, WINE_LOADER_ENTRY, entry); | 
 | 	if (!IsEqualGUID(&Desc.guidObject, &GUID_DefaultGMCollection)) { | 
 | 		pDefaultDLSEntry->bInvalidDefaultDLS = TRUE; | 
 | 	} | 
 |  | 
 | 	/* increase number of instances */ | 
 | 	InterlockedIncrement (&dwDirectMusicLoader); | 
 | 	 | 
 | 	return IDirectMusicLoaderImpl_IDirectMusicLoader_QueryInterface ((LPDIRECTMUSICLOADER8)obj, lpcGUID, ppobj); | 
 | } | 
 |  | 
 | /* help function for retrieval of search path and caching option for certain class */ | 
 | static HRESULT DMUSIC_GetLoaderSettings (LPDIRECTMUSICLOADER8 iface, REFGUID pClassID, WCHAR* wszSearchPath, LPBOOL pbCache) { | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	struct list *pEntry; | 
 | 	TRACE(": (%p, %s, %p, %p)\n", This, debugstr_dmguid(pClassID), wszSearchPath, pbCache); | 
 | 	 | 
 | 	LIST_FOR_EACH(pEntry, This->pClassSettings) { | 
 | 		LPWINE_LOADER_OPTION pOptionEntry = LIST_ENTRY(pEntry, WINE_LOADER_OPTION, entry); | 
 | 		if (IsEqualCLSID (pClassID, &pOptionEntry->guidClass)) { | 
 | 			if (wszSearchPath) | 
 | 				strcpyW(wszSearchPath, pOptionEntry->wszSearchPath); | 
 | 			if (pbCache) | 
 | 				*pbCache = pOptionEntry->bCache; | 
 | 			return S_OK; | 
 | 		} | 
 | 	} | 
 | 	return S_FALSE; | 
 | } | 
 |  | 
 | /* help function for setting search path and caching option for certain class */ | 
 | static HRESULT DMUSIC_SetLoaderSettings (LPDIRECTMUSICLOADER8 iface, REFGUID pClassID, WCHAR* wszSearchPath, LPBOOL pbCache) { | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	struct list *pEntry; | 
 | 	HRESULT result = S_FALSE; /* in case pClassID != GUID_DirectMusicAllTypes and not a valid CLSID */ | 
 | 	TRACE(": (%p, %s, %p, %p)\n", This, debugstr_dmguid(pClassID), wszSearchPath, pbCache); | 
 | 	 | 
 | 	LIST_FOR_EACH(pEntry, This->pClassSettings) { | 
 | 		LPWINE_LOADER_OPTION pOptionEntry = LIST_ENTRY(pEntry, WINE_LOADER_OPTION, entry); | 
 | 		/* well, either we have GUID_DirectMusicAllTypes and need to set it to all, | 
 | 		   or specific CLSID is given and we set it only to it */ | 
 | 		if (IsEqualGUID (pClassID, &GUID_DirectMusicAllTypes) || | 
 | 			IsEqualCLSID (pClassID, &pOptionEntry->guidClass)) { | 
 | 			if (wszSearchPath) | 
 | 				strcpyW(pOptionEntry->wszSearchPath, wszSearchPath); | 
 | 			if (pbCache) | 
 | 				pOptionEntry->bCache = *pbCache; | 
 | 			result = S_OK; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | static HRESULT DMUSIC_InitLoaderSettings (LPDIRECTMUSICLOADER8 iface) { | 
 | 	ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface); | 
 | 	 | 
 | 	/* hard-coded list of classes */ | 
 | 	static REFCLSID classes[] = { | 
 | 		&CLSID_DirectMusicAudioPathConfig, | 
 | 		&CLSID_DirectMusicBand, | 
 | 		&CLSID_DirectMusicContainer, | 
 | 		&CLSID_DirectMusicCollection, | 
 | 		&CLSID_DirectMusicChordMap, | 
 | 		&CLSID_DirectMusicSegment, | 
 | 		&CLSID_DirectMusicScript, | 
 | 		&CLSID_DirectMusicSong, | 
 | 		&CLSID_DirectMusicStyle, | 
 | 		&CLSID_DirectMusicGraph, | 
 | 		&CLSID_DirectSoundWave | 
 | 	}; | 
 | 	 | 
 | 	unsigned int i; | 
 | 	WCHAR wszCurrent[MAX_PATH]; | 
 |  | 
 | 	TRACE(": (%p)\n", This); | 
 | 	GetCurrentDirectoryW (MAX_PATH, wszCurrent); | 
 |  | 
 | 	for (i = 0; i < sizeof(classes)/sizeof(REFCLSID); i++) { | 
 | 		LPWINE_LOADER_OPTION pNewSetting = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(WINE_LOADER_OPTION)); | 
 | 		pNewSetting->guidClass = *classes[i]; | 
 | 		strcpyW (pNewSetting->wszSearchPath, wszCurrent); | 
 | 		pNewSetting->bCache = TRUE; | 
 | 		list_add_tail (This->pClassSettings, &pNewSetting->entry); | 
 | 	} | 
 |  | 
 | 	return S_OK; | 
 | } |