quartz: Fix memory leak and behavior on errors in FilterMapper_EnumMatchingFilters.
[wine] / dlls / dmloader / loader.c
1 /* IDirectMusicLoaderImpl
2  *
3  * Copyright (C) 2003-2004 Rok Mandeljc
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 #include "dmloader_private.h"
21
22 WINE_DEFAULT_DEBUG_CHANNEL(dmloader);
23
24 static HRESULT WINAPI DMUSIC_SetLoaderSettings (LPDIRECTMUSICLOADER8 iface, REFGUID pClassID, WCHAR* wszSearchPath, LPBOOL pbCache);
25
26 /*****************************************************************************
27  * IDirectMusicLoaderImpl implementation
28  */
29 /* IUnknown/IDirectMusicLoader(8) part: */
30 static ULONG WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_AddRef (LPDIRECTMUSICLOADER8 iface);
31
32 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_QueryInterface (LPDIRECTMUSICLOADER8 iface, REFIID riid, LPVOID *ppobj) {
33         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
34
35         TRACE("(%p, %s, %p)\n",This, debugstr_dmguid(riid), ppobj);
36         if (IsEqualIID (riid, &IID_IUnknown) || 
37             IsEqualIID (riid, &IID_IDirectMusicLoader) ||
38             IsEqualIID (riid, &IID_IDirectMusicLoader8)) {
39                 IDirectMusicLoaderImpl_IDirectMusicLoader_AddRef (iface);
40                 *ppobj = This;
41                 return S_OK;
42         }
43         
44         WARN(": not found\n");
45         return E_NOINTERFACE;
46 }
47
48 static ULONG WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_AddRef (LPDIRECTMUSICLOADER8 iface) {
49         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
50         TRACE("(%p): AddRef from %d\n", This, This->dwRef);
51         return InterlockedIncrement (&This->dwRef);
52 }
53
54 static ULONG WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_Release (LPDIRECTMUSICLOADER8 iface) {
55         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
56         
57         DWORD dwRef = InterlockedDecrement (&This->dwRef);
58         TRACE("(%p): ReleaseRef to %d\n", This, This->dwRef);
59         if (dwRef == 0) {
60                 DMUSIC_DestroyDirectMusicLoaderImpl (iface);
61                 HeapFree (GetProcessHeap(), 0, This);
62         }
63         
64         return dwRef;
65 }
66
67 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_GetObject (LPDIRECTMUSICLOADER8 iface, LPDMUS_OBJECTDESC pDesc, REFIID riid, LPVOID* ppv) {
68         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
69         HRESULT result = S_OK;
70         HRESULT ret = S_OK; /* used at the end of function, to determine whether everything went OK */
71         
72         struct list *pEntry;
73         LPWINE_LOADER_ENTRY pObjectEntry = NULL;
74         LPSTREAM pStream;
75         IPersistStream* pPersistStream = NULL;
76
77         LPDIRECTMUSICOBJECT pObject;
78         DMUS_OBJECTDESC GotDesc;
79         BOOL bCache;
80
81         TRACE("(%p, %p, %s, %p): pDesc:\n%s\n", This, pDesc, debugstr_dmguid(riid), ppv, debugstr_DMUS_OBJECTDESC(pDesc));
82         
83         /* sometimes it happens that guidClass is missing... which is a BadThingTM */
84         if (!(pDesc->dwValidData & DMUS_OBJ_CLASS)) {
85           ERR(": guidClass not valid but needed\n");
86           *ppv = NULL;
87           return DMUS_E_LOADER_NOCLASSID;
88         }
89         
90         /* OK, first we iterate thru the list of objects we know about; these are either loaded (GetObject, LoadObjectFromFile)
91            or set via SetObject; */
92         TRACE(": looking if we have object in the cache or if it can be found via alias\n");
93         LIST_FOR_EACH(pEntry, This->pObjects) {
94                 LPWINE_LOADER_ENTRY pExistingEntry = LIST_ENTRY(pEntry, WINE_LOADER_ENTRY, entry);
95                 if ((pDesc->dwValidData & DMUS_OBJ_OBJECT) &&
96                         (pExistingEntry->Desc.dwValidData & DMUS_OBJ_OBJECT) &&
97                         IsEqualGUID (&pDesc->guidObject, &pExistingEntry->Desc.guidObject)) {
98                         TRACE(": found it by object GUID\n");
99                         /* I suppose such stuff can happen only when GUID for object is given (GUID_DefaultGMCollection) */
100                         if (pExistingEntry->bInvalidDefaultDLS) {
101                                 TRACE(": found faulty default DLS collection... enabling M$ compliant behaviour\n");
102                                 return DMUS_E_LOADER_NOFILENAME;        
103                         }
104                         if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
105                                 TRACE(": already loaded\n");
106                                 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
107                         } else {
108                                 TRACE(": not loaded yet\n");
109                                 pObjectEntry = pExistingEntry;
110                         }
111                 }
112                 else if ((pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
113                                 (pExistingEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
114                                 !strncmpW (pDesc->wszFileName, pExistingEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
115                         TRACE(": found it by fullpath filename\n");
116                         if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
117                                 TRACE(": already loaded\n");
118                                 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
119                         } else {
120                                 TRACE(": not loaded yet\n");
121                                 pObjectEntry = pExistingEntry;
122                         }
123                 }
124                 else if ((pDesc->dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
125                                 (pExistingEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
126                                 !strncmpW (pDesc->wszName, pExistingEntry->Desc.wszName, DMUS_MAX_NAME) &&
127                                 !strncmpW (pDesc->wszCategory, pExistingEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) {
128                         TRACE(": found it by name and category\n");
129                         if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
130                                 TRACE(": already loaded\n");
131                                 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
132                         } else {
133                                 TRACE(": not loaded yet\n");
134                                 pObjectEntry = pExistingEntry;
135                         }
136                 }
137                 else if ((pDesc->dwValidData & DMUS_OBJ_NAME) &&
138                                 (pExistingEntry->Desc.dwValidData & DMUS_OBJ_NAME) &&
139                                 !strncmpW (pDesc->wszName, pExistingEntry->Desc.wszName, DMUS_MAX_NAME)) {
140                         TRACE(": found it by name\n");
141                         if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
142                                 TRACE(": already loaded\n");
143                                 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
144                         } else {
145                                 TRACE(": not loaded yet\n");
146                                 pObjectEntry = pExistingEntry;
147                         }
148                 }
149                 else if ((pDesc->dwValidData & DMUS_OBJ_FILENAME) &&
150                                 (pExistingEntry->Desc.dwValidData & DMUS_OBJ_FILENAME) &&
151                                 !strncmpW (pDesc->wszFileName, pExistingEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
152                         TRACE(": found it by filename\n");                              
153                         if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
154                                 TRACE(": already loaded\n");
155                                 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
156                         } else {
157                                 TRACE(": not loaded yet\n");
158                                 pObjectEntry = pExistingEntry;
159                         }
160                 }
161         }
162         
163         /* basically, if we found alias, we use its descriptor to load...
164            else we use info we were given */
165         if (pObjectEntry) {
166                 TRACE(": found alias entry for requested object... using stored info\n");
167                 /* I think in certain cases it can happen that entry's descriptor lacks info
168                    where to load from (e.g.: if we loaded from stream and then released object
169                    from cache; then only it's CLSID, GUID and perhaps name are left); so just
170                    overwrite info entry has (since it ought to be 100% correct) */
171                 DMUSIC_CopyDescriptor (pDesc, &pObjectEntry->Desc);
172                 /*pDesc = &pObjectEntry->Desc; */ /* FIXME: is this OK? */
173         } else {
174                 TRACE(": no cache/alias entry found for requested object\n");
175         }
176         
177         if (pDesc->dwValidData & DMUS_OBJ_URL) {
178                 TRACE(": loading from URLs not supported yet\n");
179                 return DMUS_E_LOADER_FORMATNOTSUPPORTED;
180         }
181         else if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
182                 /* load object from file */
183                 /* generate filename; if it's full path, don't add search 
184                    directory path, otherwise do */
185                 WCHAR wszFileName[MAX_PATH];
186
187                 if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
188                         lstrcpyW(wszFileName, pDesc->wszFileName);
189                 } else {
190                         WCHAR *p, wszSearchPath[MAX_PATH];
191                         DMUSIC_GetLoaderSettings (iface, &pDesc->guidClass, wszSearchPath, NULL);
192                         lstrcpyW(wszFileName, wszSearchPath);
193                         p = wszFileName + lstrlenW(wszFileName);
194                         if (p > wszFileName && p[-1] != '\\') *p++ = '\\';
195                         strcpyW(p, pDesc->wszFileName);
196                 }
197                 TRACE(": loading from file (%s)\n", debugstr_w(wszFileName));
198                 /* create stream and associate it with file */                  
199                 result = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream);
200                 if (FAILED(result)) {
201                         ERR(": could not create file stream\n");
202                         return result;
203                 }
204                 result = IDirectMusicLoaderFileStream_Attach (pStream, wszFileName, iface);
205                 if (FAILED(result)) {
206                         ERR(": could not attach stream to file\n");
207                         IStream_Release (pStream);
208                         return result;
209                 }
210         }
211         else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) {
212                 /* load object from resource */
213                 TRACE(": loading from resource\n");
214                 /* create stream and associate it with given resource */                        
215                 result = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pStream);
216                 if (FAILED(result)) {
217                         ERR(": could not create resource stream\n");
218                         return result;
219                 }
220                 result = IDirectMusicLoaderResourceStream_Attach (pStream, pDesc->pbMemData, pDesc->llMemLength, 0, iface);
221                 if (FAILED(result)) {
222                         ERR(": could not attach stream to resource\n");
223                         IStream_Release (pStream);
224                         return result;
225                 }
226         }
227         else if (pDesc->dwValidData & DMUS_OBJ_STREAM) {
228                 /* load object from stream */
229                 TRACE(": loading from stream\n");
230                 /* create universal stream and associate it with given one */                   
231                 result = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pStream);
232                 if (FAILED(result)) {
233                         ERR(": could not create generic stream\n");
234                         return result;
235                 }
236                 result = IDirectMusicLoaderGenericStream_Attach (pStream, pDesc->pStream, iface);
237                 if (FAILED(result)) {
238                         ERR(": failed to attach stream\n");
239                         IStream_Release (pStream);
240                         return result;
241                 }
242         } else {
243                 /* nowhere to load from */
244                 FIXME(": unknown/unsupported way of loading\n");
245                 return DMUS_E_LOADER_NOFILENAME; /* test shows this is returned */
246         }
247
248         /* create object */
249         result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject);
250         if (FAILED(result)) {
251                 ERR(": could not create object\n");
252                 return result;
253         }
254         /* acquire PersistStream interface */
255         result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*)&pPersistStream);
256         if (FAILED(result)) {
257                 ERR("failed to Query\n");
258                 return result;
259         }
260         /* load */
261         result = IPersistStream_Load (pPersistStream, pStream);
262         if (result != S_OK) {
263                 WARN(": failed to (completely) load object (%s)\n", debugstr_dmreturn(result));
264                 ret = DMUS_S_PARTIALLOAD /*result*/;
265         }
266         /* get descriptor */
267         DM_STRUCT_INIT(&GotDesc);
268         result = IDirectMusicObject_GetDescriptor (pObject, &GotDesc);
269         /* set filename (if we loaded via filename) */
270         if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
271                 GotDesc.dwValidData |= (pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH));
272                 strcpyW (GotDesc.wszFileName, pDesc->wszFileName);
273         }
274         if (FAILED(result)) {
275                 ERR(": failed to get descriptor\n");
276                 return result;
277         }
278         /* release all loading related stuff */
279         IStream_Release (pStream);
280         IPersistStream_Release (pPersistStream);        
281                 
282         /* add object to cache/overwrite existing info (if cache is enabled) */
283         DMUSIC_GetLoaderSettings (iface, &pDesc->guidClass, NULL, &bCache);
284         if (bCache) {
285                 if (!pObjectEntry) {
286                         pObjectEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(WINE_LOADER_ENTRY));
287                         DM_STRUCT_INIT(&pObjectEntry->Desc);
288                         if (pObject) {
289                                 DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc);
290                                 pObjectEntry->pObject = pObject;
291                                 pObjectEntry->bInvalidDefaultDLS = FALSE;
292                         }
293                         list_add_head (This->pObjects, &pObjectEntry->entry);
294                 } else {
295                         if (pObject) {
296                                 DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc);
297                                 pObjectEntry->pObject = pObject;
298                                 pObjectEntry->bInvalidDefaultDLS = FALSE;
299                         }               
300                 }
301                 TRACE(": filled in cache entry\n");
302         } else TRACE(": caching disabled\n");
303
304 #if 0
305         /* for debug purposes (e.g. to check if all files are cached) */
306         TRACE("*** Loader's cache ***\n");
307         int i = 0;
308         LIST_FOR_EACH (pEntry, This->pObjects) {
309                 i++;
310                 pObjectEntry = LIST_ENTRY(pEntry, WINE_LOADER_ENTRY, entry);
311                 TRACE(": entry nr. %i:\n%s\n  - bInvalidDefaultDLS = %i\n  - pObject = %p\n", i, debugstr_DMUS_OBJECTDESC(&pObjectEntry->Desc), pObjectEntry->bInvalidDefaultDLS, pObjectEntry->pObject);
312         }
313 #endif
314         
315         result = IDirectMusicObject_QueryInterface (pObject, riid, ppv);
316         if (!bCache) IDirectMusicObject_Release (pObject); /* since loader's reference is not needed */
317         /* if there was trouble with loading, and if no other error occurred,
318            we should return DMUS_S_PARTIALLOAD; else, error is returned */
319         if (result == S_OK)
320                 return ret;
321         else
322                 return result;
323 }
324
325 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_SetObject (LPDIRECTMUSICLOADER8 iface, LPDMUS_OBJECTDESC pDesc) {
326         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
327         LPSTREAM pStream;
328         LPDIRECTMUSICOBJECT pObject;
329         DMUS_OBJECTDESC Desc;
330         struct list *pEntry;
331         LPWINE_LOADER_ENTRY pObjectEntry, pNewEntry;
332         HRESULT hr;
333
334         TRACE("(%p, %p): pDesc:\n%s\n", This, pDesc, debugstr_DMUS_OBJECTDESC(pDesc));
335
336         /* create stream and load additional info from it */
337         if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
338                 /* generate filename; if it's full path, don't add search 
339                    directory path, otherwise do */
340                 WCHAR wszFileName[MAX_PATH];
341
342                 if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
343                         lstrcpyW(wszFileName, pDesc->wszFileName);
344                 } else {
345                         WCHAR *p;
346                         WCHAR wszSearchPath[MAX_PATH];
347                         DMUSIC_GetLoaderSettings (iface, &pDesc->guidClass, wszSearchPath, NULL);
348                         lstrcpyW(wszFileName, wszSearchPath);
349                         p = wszFileName + lstrlenW(wszFileName);
350                         if (p > wszFileName && p[-1] != '\\') *p++ = '\\';
351                         strcpyW(p, pDesc->wszFileName);
352                 }
353                 /* create stream */
354                 hr = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream);
355                 if (FAILED(hr)) {
356                         ERR(": could not create file stream\n");
357                         return DMUS_E_LOADER_FAILEDOPEN;
358                 }
359                 /* attach stream */
360                 hr = IDirectMusicLoaderFileStream_Attach (pStream, wszFileName, iface);
361                 if (FAILED(hr)) {
362                         ERR(": could not attach stream to file\n");
363                         IStream_Release (pStream);
364                         return DMUS_E_LOADER_FAILEDOPEN;
365                 }
366         }
367         else if (pDesc->dwValidData & DMUS_OBJ_STREAM) {        
368                 /* create stream */
369                 hr = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pStream);
370                 if (FAILED(hr)) {
371                         ERR(": could not create generic stream\n");
372                         return DMUS_E_LOADER_FAILEDOPEN;
373                 }
374                 /* attach stream */
375                 hr = IDirectMusicLoaderGenericStream_Attach (pStream, pDesc->pStream, iface);
376                 if (FAILED(hr)) {
377                         ERR(": could not attach stream\n");
378                         IStream_Release (pStream);
379                         return DMUS_E_LOADER_FAILEDOPEN;
380                 }
381         }
382         else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) {
383                 /* create stream */
384                 hr = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pStream);
385                 if (FAILED(hr)) {
386                         ERR(": could not create resource stream\n");
387                         return DMUS_E_LOADER_FAILEDOPEN;
388                 }
389                 /* attach stream */
390                 hr = IDirectMusicLoaderResourceStream_Attach (pStream, pDesc->pbMemData, pDesc->llMemLength, 0, iface);
391                 if (FAILED(hr)) {
392                         ERR(": could not attach stream to resource\n");
393                         IStream_Release (pStream);
394                         return DMUS_E_LOADER_FAILEDOPEN;
395                 }
396         }
397         else {
398                 ERR(": no way to get additional info\n");
399                 return DMUS_E_LOADER_FAILEDOPEN;
400         }
401
402         /* create object */
403         CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject);
404
405         /* *sigh*... some ms objects have lousy implementation of ParseDescriptor that clears input descriptor :( */
406 #ifdef NOW_WE_ARE_FREE
407         /* parse descriptor: we actually use input descriptor; fields that aren't available from stream remain,
408            otherwise real info is set */
409          IDirectMusicObject_ParseDescriptor (pObject, pStream, pDesc);
410 #endif
411         /* hmph... due to some trouble I had with certain tests, we store current position and then set it back */
412         DM_STRUCT_INIT(&Desc);
413         if (FAILED(IDirectMusicObject_ParseDescriptor (pObject, pStream, &Desc))) {
414                 ERR(": couldn't parse descriptor\n");
415                 return DMUS_E_LOADER_FORMATNOTSUPPORTED;
416         }
417
418         /* copy elements from parsed descriptor into input descriptor; this sets new info, overwriting if necessary,
419            but leaves info that's provided by input and not available from stream */    
420         DMUSIC_CopyDescriptor (pDesc, &Desc);
421         
422         /* release everything */
423         IDirectMusicObject_Release (pObject);
424         IStream_Release (pStream);      
425         
426         /* sometimes it happens that twisted programs call SetObject for same object twice...
427            in such cases, native loader returns S_OK and does nothing... a sound plan */
428         LIST_FOR_EACH (pEntry, This->pObjects) {
429                 pObjectEntry = LIST_ENTRY (pEntry, WINE_LOADER_ENTRY, entry);
430                 if (!memcmp (&pObjectEntry->Desc, pDesc, sizeof(DMUS_OBJECTDESC))) {
431                         TRACE(": exacly same entry already exists\n");
432                         return S_OK;
433                 }
434         }               
435         
436         /* add new entry */
437         TRACE(": adding alias entry with following info: \n%s\n", debugstr_DMUS_OBJECTDESC(pDesc));
438         pNewEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(WINE_LOADER_ENTRY));
439         /* use this function instead of pure memcpy due to streams (memcpy just copies pointer), 
440            which is basically used further by app that called SetDescriptor... better safety than exception */
441         DMUSIC_CopyDescriptor (&pNewEntry->Desc, pDesc);
442         list_add_head (This->pObjects, &pNewEntry->entry);
443
444         return S_OK;
445 }
446
447 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_SetSearchDirectory (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, WCHAR* pwzPath, BOOL fClear) {
448         WCHAR wszCurrentPath[MAX_PATH];
449         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
450         TRACE("(%p, %s, %s, %d)\n", This, debugstr_dmguid(rguidClass), debugstr_w(pwzPath), fClear);
451         FIXME(": fClear ignored\n");
452         DMUSIC_GetLoaderSettings (iface, rguidClass, wszCurrentPath, NULL);
453         if (!strncmpW(wszCurrentPath, pwzPath, MAX_PATH)) {
454           return S_FALSE;
455         }
456         /* FIXME: check if path is valid; else return DMUS_E_LOADER_BADPATH */
457         return DMUSIC_SetLoaderSettings (iface, rguidClass, pwzPath, NULL);
458 }
459
460 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_ScanDirectory (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, WCHAR* pwzFileExtension, WCHAR* pwzScanFileName) {
461         static const WCHAR wszAny[] = {'*',0};
462         WIN32_FIND_DATAW FileData;
463         HANDLE hSearch;
464         WCHAR wszSearchString[MAX_PATH];
465         WCHAR *p;
466         HRESULT result;
467         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
468         TRACE("(%p, %s, %p, %p)\n", This, debugstr_dmguid(rguidClass), pwzFileExtension, pwzScanFileName);
469         if (IsEqualGUID (rguidClass, &GUID_DirectMusicAllTypes) || !DMUSIC_IsValidLoadableClass(rguidClass)) {
470                 ERR(": rguidClass invalid CLSID\n");
471                 return REGDB_E_CLASSNOTREG;
472         }
473         
474         /* get search path for given class */
475         DMUSIC_GetLoaderSettings (iface, rguidClass, wszSearchString, NULL);
476         
477         p = wszSearchString + lstrlenW(wszSearchString);
478         if (p > wszSearchString && p[-1] != '\\') *p++ = '\\';
479         *p++ = '*'; /* any file */
480         if (strcmpW (pwzFileExtension, wszAny)) *p++ = '.'; /* if we have actual extension, put a dot */
481         strcpyW (p, pwzFileExtension);
482         
483         TRACE(": search string: %s\n", debugstr_w(wszSearchString));
484         
485         hSearch = FindFirstFileW (wszSearchString, &FileData);
486         if (hSearch == INVALID_HANDLE_VALUE) {
487                 TRACE(": no files found\n");
488                 return S_FALSE;
489         }
490         
491         do {
492                 DMUS_OBJECTDESC Desc;
493                 DM_STRUCT_INIT(&Desc);
494                 Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_DATE;
495                 Desc.guidClass = *rguidClass;
496                 strcpyW (Desc.wszFileName, FileData.cFileName);
497                 FileTimeToLocalFileTime (&FileData.ftCreationTime, &Desc.ftDate);
498                 IDirectMusicLoader8_SetObject (iface, &Desc);
499                 
500                 if (!FindNextFileW (hSearch, &FileData)) {
501                         if (GetLastError () == ERROR_NO_MORE_FILES) {
502                                 TRACE(": search completed\n");
503                                 result = S_OK;
504                         } else {
505                                 ERR(": could not get next file\n");
506                                 result = E_FAIL;
507                         }
508                         FindClose (hSearch);
509                         return result;
510                 }
511         } while (1);
512 }
513
514 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_CacheObject (LPDIRECTMUSICLOADER8 iface, IDirectMusicObject* pObject) {
515         DMUS_OBJECTDESC Desc;
516         HRESULT result = DMUS_E_LOADER_OBJECTNOTFOUND;
517         struct list *pEntry;
518         LPWINE_LOADER_ENTRY  pObjectEntry = NULL;
519
520         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
521         TRACE("(%p, %p)\n", This, pObject);
522         
523         /* get descriptor */
524         DM_STRUCT_INIT(&Desc);
525         IDirectMusicObject_GetDescriptor (pObject, &Desc);
526         
527         /* now iterate thru list and check if we have alias (without object), corresponding
528            to descriptor of input object */
529         LIST_FOR_EACH(pEntry, This->pObjects) {
530                 pObjectEntry = LIST_ENTRY(pEntry, WINE_LOADER_ENTRY, entry);
531                 if ((Desc.dwValidData & DMUS_OBJ_OBJECT) &&
532                         (pObjectEntry->Desc.dwValidData & DMUS_OBJ_OBJECT) &&
533                         IsEqualGUID (&Desc.guidObject, &pObjectEntry->Desc.guidObject)) {
534                         TRACE(": found it by object GUID\n");
535                         if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
536                                 result = S_FALSE;
537                         else
538                                 result = S_OK;
539                         break;
540                 }
541                 else if ((Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
542                                 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
543                                 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
544                         TRACE(": found it by fullpath filename\n");
545                         if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
546                                 result = S_FALSE;
547                         else
548                                 result = S_OK;
549                         break;
550                 }
551                 else if ((Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
552                                 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
553                                 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME) &&
554                                 !strncmpW (Desc.wszCategory, pObjectEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) {
555                         TRACE(": found it by name and category\n");
556                         if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
557                                 result = S_FALSE;
558                         else
559                                 result = S_OK;
560                         break;
561                 }
562                 else if ((Desc.dwValidData & DMUS_OBJ_NAME) &&
563                                 (pObjectEntry->Desc.dwValidData & DMUS_OBJ_NAME) &&
564                                 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME)) {
565                         TRACE(": found it by name\n");
566                         if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
567                                 result = S_FALSE;
568                         else
569                                 result = S_OK;
570                         break;
571                 }
572                 else if ((Desc.dwValidData & DMUS_OBJ_FILENAME) &&
573                                 (pObjectEntry->Desc.dwValidData & DMUS_OBJ_FILENAME) &&
574                                 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
575                         TRACE(": found it by filename\n");                              
576                         if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
577                                 result = S_FALSE;
578                         else
579                                 result = S_OK;
580                         break;
581                 }
582         }
583         
584         /* if we found such alias, then set everything */
585         if (result == S_OK) {
586                 pObjectEntry->Desc.dwValidData &= DMUS_OBJ_LOADED;
587                 pObjectEntry->pObject = pObject;
588                 IDirectMusicObject_AddRef (pObjectEntry->pObject);
589         }
590         
591         return result;
592 }
593
594 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_ReleaseObject (LPDIRECTMUSICLOADER8 iface, IDirectMusicObject* pObject) {
595         DMUS_OBJECTDESC Desc;
596         struct list *pEntry;
597         LPWINE_LOADER_ENTRY pObjectEntry = NULL;
598         HRESULT result = S_FALSE;
599
600         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
601         TRACE("(%p, %p)\n", This, pObject);
602         
603         /* get descriptor */
604         DM_STRUCT_INIT(&Desc);
605         IDirectMusicObject_GetDescriptor (pObject, &Desc);
606         
607         /* iterate thru the list of objects we know about; check only those with DMUS_OBJ_LOADED */
608         TRACE(": looking for the object in cache\n");
609         LIST_FOR_EACH(pEntry, This->pObjects) {
610                 pObjectEntry = LIST_ENTRY(pEntry, WINE_LOADER_ENTRY, entry);
611                 if ((Desc.dwValidData & DMUS_OBJ_OBJECT) &&
612                         (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_OBJECT | DMUS_OBJ_LOADED)) &&
613                         IsEqualGUID (&Desc.guidObject, &pObjectEntry->Desc.guidObject)) {
614                         TRACE(": found it by object GUID\n%s\n", debugstr_DMUS_OBJECTDESC(&pObjectEntry->Desc));
615                         result = S_OK;
616                         break;
617                 }
618                 else if ((Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
619                                 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_LOADED)) &&
620                                 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
621                         TRACE(": found it by fullpath filename\n");
622                         result = S_OK;                  
623                         break;
624                 }
625                 else if ((Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
626                                 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY | DMUS_OBJ_LOADED)) &&
627                                 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME) &&
628                                 !strncmpW (Desc.wszCategory, pObjectEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) {
629                         TRACE(": found it by name and category\n");
630                         result = S_OK;                  
631                         break;
632                 }
633                 else if ((Desc.dwValidData & DMUS_OBJ_NAME) &&
634                                 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_LOADED)) &&
635                                 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME)) {
636                         TRACE(": found it by name\n");
637                         result = S_OK;
638                         break;
639                 }
640                 else if ((Desc.dwValidData & DMUS_OBJ_FILENAME) &&
641                                 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_LOADED)) &&
642                                 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
643                         TRACE(": found it by filename\n");
644                         result = S_OK;                  
645                         break;
646                 }
647         }
648         if (result == S_OK) {
649                 /*TRACE(": releasing: \n%s  - bInvalidDefaultDLS = %i\n  - pObject = %p\n", debugstr_DMUS_OBJECTDESC(&pObjectEntry->Desc), pObjectEntry->bInvalidDefaultDLS, pObjectEntry->pObject); */
650                 IDirectMusicObject_Release (pObjectEntry->pObject);
651                 pObjectEntry->pObject = NULL;
652                 pObjectEntry->Desc.dwValidData &= ~DMUS_OBJ_LOADED;
653         } 
654         return result;
655 }
656
657 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_ClearCache (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass) {
658         struct list *pEntry;
659         LPWINE_LOADER_ENTRY pObjectEntry;
660         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
661         TRACE("(%p, %s)\n", This, debugstr_dmguid(rguidClass));
662         
663         LIST_FOR_EACH (pEntry, This->pObjects) {
664                 pObjectEntry = LIST_ENTRY (pEntry, WINE_LOADER_ENTRY, entry);
665                 
666                 if ((IsEqualGUID (rguidClass, &GUID_DirectMusicAllTypes) || IsEqualGUID (rguidClass, &pObjectEntry->Desc.guidClass)) &&
667                         (pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED)) {
668                         /* basically, wrap to ReleaseObject for each object found */
669                         IDirectMusicLoader8_ReleaseObject (iface, pObjectEntry->pObject);
670                 }
671         }
672         
673         return S_OK;
674 }
675
676 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_EnableCache (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, BOOL fEnable) {
677         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
678         BOOL bCurrent;
679         TRACE("(%p, %s, %d)\n", This, debugstr_dmguid(rguidClass), fEnable);
680         DMUSIC_GetLoaderSettings (iface, rguidClass, NULL, &bCurrent);
681         if (bCurrent == fEnable)
682                 return S_FALSE;
683         else
684                 return DMUSIC_SetLoaderSettings (iface, rguidClass, NULL, &fEnable);
685 }
686
687 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_EnumObject (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, DWORD dwIndex, LPDMUS_OBJECTDESC pDesc) {
688         DWORD dwCount = 0;
689         struct list *pEntry;
690         LPWINE_LOADER_ENTRY pObjectEntry;
691         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
692         TRACE("(%p, %s, %d, %p)\n", This, debugstr_dmguid(rguidClass), dwIndex, pDesc);
693
694         DM_STRUCT_INIT(pDesc);
695
696         LIST_FOR_EACH (pEntry, This->pObjects) {
697                 pObjectEntry = LIST_ENTRY (pEntry, WINE_LOADER_ENTRY, entry);
698
699                 if (IsEqualGUID (rguidClass, &GUID_DirectMusicAllTypes) || IsEqualGUID (rguidClass, &pObjectEntry->Desc.guidClass)) {
700                         if (dwCount == dwIndex) {
701                                 *pDesc = pObjectEntry->Desc;
702                                 /* we aren't supposed to reveal this info */
703                                 pDesc->dwValidData &= ~(DMUS_OBJ_MEMORY | DMUS_OBJ_STREAM);
704                                 pDesc->pbMemData = NULL;
705                                 pDesc->llMemLength = 0;
706                                 pDesc->pStream = NULL;
707                                 return S_OK;
708                         }
709                         dwCount++;
710                 }
711         }
712         
713         TRACE(": not found\n"); 
714         return S_FALSE;
715 }
716
717 static void WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_CollectGarbage (LPDIRECTMUSICLOADER8 iface) {
718         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
719         FIXME("(%p): stub\n", This);
720 }
721
722 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_ReleaseObjectByUnknown (LPDIRECTMUSICLOADER8 iface, IUnknown* pObject) {
723         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
724         HRESULT result;
725         LPDIRECTMUSICOBJECT pObjectInterface;
726         
727         TRACE("(%p, %p)\n", This, pObject);
728         
729         if (IsBadReadPtr (pObject, sizeof(LPUNKNOWN))) {
730                 ERR(": pObject bad write pointer\n");
731                 return E_POINTER;
732         }
733         /* we simply get IDirectMusicObject interface */
734         result = IUnknown_QueryInterface (pObject, &IID_IDirectMusicObject, (LPVOID*)&pObjectInterface);
735         if (FAILED(result)) return result;
736         /* and release it in old-fashioned way */
737         result = IDirectMusicLoader8_ReleaseObject (iface, pObjectInterface);
738         IDirectMusicObject_Release (pObjectInterface);
739         
740         return result;
741 }
742
743 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_LoadObjectFromFile (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClassID, REFIID iidInterfaceID, WCHAR* pwzFilePath, void** ppObject) {
744         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
745         DMUS_OBJECTDESC ObjDesc;
746         WCHAR wszLoaderSearchPath[MAX_PATH];
747
748         TRACE("(%p, %s, %s, %s, %p): wrapping to IDirectMusicLoaderImpl_GetObject\n", This, debugstr_dmguid(rguidClassID), debugstr_dmguid(iidInterfaceID), debugstr_w(pwzFilePath), ppObject);
749
750         DM_STRUCT_INIT(&ObjDesc);       
751         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 */
752         ObjDesc.guidClass = *rguidClassID;
753         /* OK, MSDN says that search order is the following:
754             - current directory (DONE)
755             - windows search path (FIXME: how do I get that?)
756             - loader's search path (DONE)
757         */
758         DMUSIC_GetLoaderSettings (iface, rguidClassID, wszLoaderSearchPath, NULL);
759     /* search in current directory */
760         if (!SearchPathW (NULL, pwzFilePath, NULL, sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL) &&
761         /* search in loader's search path */
762                 !SearchPathW (wszLoaderSearchPath, pwzFilePath, NULL, sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL)) {
763                 /* cannot find file */
764                 TRACE(": cannot find file\n");
765                 return DMUS_E_LOADER_FAILEDOPEN;
766         }
767         
768         TRACE(": full file path = %s\n", debugstr_w (ObjDesc.wszFileName));
769         
770         return IDirectMusicLoaderImpl_IDirectMusicLoader_GetObject (iface, &ObjDesc, iidInterfaceID, ppObject);
771 }
772
773 static const IDirectMusicLoader8Vtbl DirectMusicLoader_Loader_Vtbl = {
774         IDirectMusicLoaderImpl_IDirectMusicLoader_QueryInterface,
775         IDirectMusicLoaderImpl_IDirectMusicLoader_AddRef,
776         IDirectMusicLoaderImpl_IDirectMusicLoader_Release,
777         IDirectMusicLoaderImpl_IDirectMusicLoader_GetObject,
778         IDirectMusicLoaderImpl_IDirectMusicLoader_SetObject,
779         IDirectMusicLoaderImpl_IDirectMusicLoader_SetSearchDirectory,
780         IDirectMusicLoaderImpl_IDirectMusicLoader_ScanDirectory,
781         IDirectMusicLoaderImpl_IDirectMusicLoader_CacheObject,
782         IDirectMusicLoaderImpl_IDirectMusicLoader_ReleaseObject,
783         IDirectMusicLoaderImpl_IDirectMusicLoader_ClearCache,
784         IDirectMusicLoaderImpl_IDirectMusicLoader_EnableCache,
785         IDirectMusicLoaderImpl_IDirectMusicLoader_EnumObject,
786         IDirectMusicLoaderImpl_IDirectMusicLoader_CollectGarbage,
787         IDirectMusicLoaderImpl_IDirectMusicLoader_ReleaseObjectByUnknown,
788         IDirectMusicLoaderImpl_IDirectMusicLoader_LoadObjectFromFile
789 };
790
791 /* for ClassFactory */
792 HRESULT WINAPI DMUSIC_CreateDirectMusicLoaderImpl (LPCGUID lpcGUID, LPVOID *ppobj, LPUNKNOWN pUnkOuter) {
793         IDirectMusicLoaderImpl *obj;
794         DMUS_OBJECTDESC Desc;
795         LPWINE_LOADER_ENTRY pDefaultDLSEntry;
796         struct list *pEntry;
797
798         TRACE("(%s, %p, %p)\n", debugstr_dmguid(lpcGUID), ppobj, pUnkOuter);
799         obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoaderImpl));
800         if (NULL == obj) {
801                 *ppobj = (LPDIRECTMUSICLOADER8)NULL;
802                 return E_OUTOFMEMORY;
803         }
804         obj->LoaderVtbl = &DirectMusicLoader_Loader_Vtbl;
805         obj->dwRef = 0; /* will be inited with QueryInterface */
806         /* init critical section */
807         /* init cache/alias list */
808         /*InitializeCriticalSection (&obj->CritSect);
809         obj->CritSect.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": IDirectMusicLoaderImpl.CritSect"); */
810         obj->pObjects = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(struct list));
811         list_init (obj->pObjects);
812         /* init settings */
813         obj->pClassSettings = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(struct list));
814         list_init (obj->pClassSettings);
815         DMUSIC_InitLoaderSettings ((LPDIRECTMUSICLOADER8)obj);
816
817         /* set default DLS collection (via SetObject... so that loading via DMUS_OBJ_OBJECT is possible) */
818         DM_STRUCT_INIT(&Desc);
819         Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_OBJECT;
820         Desc.guidClass = CLSID_DirectMusicCollection;
821         Desc.guidObject = GUID_DefaultGMCollection;
822         DMUSIC_GetDefaultGMPath (Desc.wszFileName);
823         IDirectMusicLoader_SetObject ((LPDIRECTMUSICLOADER8)obj, &Desc);
824         /* and now the workaroundTM for "invalid" default DLS; basically, 
825            my tests showed that if GUID chunk is present in default DLS 
826            collection, loader treats it as "invalid" and returns 
827            DMUS_E_LOADER_NOFILENAME for all requests for it; basically, we check 
828            if out input guidObject was overwritten */
829         pEntry = list_head (obj->pObjects);
830         pDefaultDLSEntry = LIST_ENTRY (pEntry, WINE_LOADER_ENTRY, entry);
831         if (!IsEqualGUID(&Desc.guidObject, &GUID_DefaultGMCollection)) {
832                 pDefaultDLSEntry->bInvalidDefaultDLS = TRUE;
833         }
834
835         /* increase number of instances */
836         InterlockedIncrement (&dwDirectMusicLoader);
837         
838         return IDirectMusicLoaderImpl_IDirectMusicLoader_QueryInterface ((LPDIRECTMUSICLOADER8)obj, lpcGUID, ppobj);
839 }
840
841 HRESULT WINAPI DMUSIC_DestroyDirectMusicLoaderImpl (LPDIRECTMUSICLOADER8 iface) {
842         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
843
844         TRACE("(%p)\n", This);
845         
846         /* firstly, release the cache */
847         IDirectMusicLoader8_ClearCache (iface, &GUID_DirectMusicAllTypes);
848         /* FIXME: release all allocated entries */
849         /* destroy critical section */
850         /*This->CritSect.DebugInfo->Spare[0] = 0;
851         DeleteCriticalSection (&This->CritSect); */
852         
853         /* decrease number of instances */
854         InterlockedDecrement (&dwDirectMusicLoader);
855         
856         return S_OK;
857 }
858
859 /* help function for DMUSIC_SetDefaultDLS */
860 HRESULT WINAPI DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]) {
861         HKEY hkDM;
862         DWORD returnType, sizeOfReturnBuffer = MAX_PATH;
863         char szPath[MAX_PATH];
864
865         if ((RegOpenKeyExA (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkDM) != ERROR_SUCCESS) || 
866             (RegQueryValueExA (hkDM, "GMFilePath", NULL, &returnType, (LPBYTE) szPath, &sizeOfReturnBuffer) != ERROR_SUCCESS)) {
867                 WARN(": registry entry missing\n" );
868                 return E_FAIL;
869         }
870         /* FIXME: Check return types to ensure we're interpreting data right */
871         MultiByteToWideChar (CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);
872         
873         return S_OK;
874 }
875
876 /* help function for retrieval of search path and caching option for certain class */
877 HRESULT WINAPI DMUSIC_GetLoaderSettings (LPDIRECTMUSICLOADER8 iface, REFGUID pClassID, WCHAR* wszSearchPath, LPBOOL pbCache) {
878         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
879         struct list *pEntry;
880         TRACE(": (%p, %s, %p, %p)\n", This, debugstr_dmguid(pClassID), wszSearchPath, pbCache);
881         
882         LIST_FOR_EACH(pEntry, This->pClassSettings) {
883                 LPWINE_LOADER_OPTION pOptionEntry = LIST_ENTRY(pEntry, WINE_LOADER_OPTION, entry);
884                 if (IsEqualCLSID (pClassID, &pOptionEntry->guidClass)) {
885                         if (wszSearchPath)
886                                 strcpyW(wszSearchPath, pOptionEntry->wszSearchPath);
887                         if (pbCache)
888                                 *pbCache = pOptionEntry->bCache;
889                         return S_OK;
890                 }
891         }
892         return S_FALSE;
893 }
894
895 /* help function for setting search path and caching option for certain class */
896 static HRESULT WINAPI DMUSIC_SetLoaderSettings (LPDIRECTMUSICLOADER8 iface, REFGUID pClassID, WCHAR* wszSearchPath, LPBOOL pbCache) {
897         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
898         struct list *pEntry;
899         HRESULT result = S_FALSE; /* in case pClassID != GUID_DirectMusicAllTypes and not a valid CLSID */
900         TRACE(": (%p, %s, %p, %p)\n", This, debugstr_dmguid(pClassID), wszSearchPath, pbCache);
901         
902         LIST_FOR_EACH(pEntry, This->pClassSettings) {
903                 LPWINE_LOADER_OPTION pOptionEntry = LIST_ENTRY(pEntry, WINE_LOADER_OPTION, entry);
904                 /* well, either we have GUID_DirectMusicAllTypes and need to set it to all,
905                    or specific CLSID is given and we set it only to it */
906                 if (IsEqualGUID (pClassID, &GUID_DirectMusicAllTypes) ||
907                         IsEqualCLSID (pClassID, &pOptionEntry->guidClass)) {
908                         if (wszSearchPath)
909                                 strcpyW(pOptionEntry->wszSearchPath, wszSearchPath);
910                         if (pbCache)
911                                 pOptionEntry->bCache = *pbCache;
912                         result = S_OK;
913                 }
914         }
915
916         return result;
917 }
918
919 HRESULT WINAPI DMUSIC_InitLoaderSettings (LPDIRECTMUSICLOADER8 iface) {
920         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
921         
922         /* hard-coded list of classes */
923         static REFCLSID classes[] = {
924                 &CLSID_DirectMusicAudioPathConfig,
925                 &CLSID_DirectMusicBand,
926                 &CLSID_DirectMusicContainer,
927                 &CLSID_DirectMusicCollection,
928                 &CLSID_DirectMusicChordMap,
929                 &CLSID_DirectMusicSegment,
930                 &CLSID_DirectMusicScript,
931                 &CLSID_DirectMusicSong,
932                 &CLSID_DirectMusicStyle,
933                 &CLSID_DirectMusicGraph,
934                 &CLSID_DirectSoundWave
935         };
936         
937         unsigned int i;
938         WCHAR wszCurrent[MAX_PATH];
939
940         TRACE(": (%p)\n", This);
941         GetCurrentDirectoryW (MAX_PATH, wszCurrent);
942
943         for (i = 0; i < sizeof(classes)/sizeof(REFCLSID); i++) {
944                 LPWINE_LOADER_OPTION pNewSetting = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(WINE_LOADER_OPTION));
945                 pNewSetting->guidClass = *classes[i];
946                 strcpyW (pNewSetting->wszSearchPath, wszCurrent);
947                 pNewSetting->bCache = TRUE;
948                 list_add_tail (This->pClassSettings, &pNewSetting->entry);
949         }
950
951         return S_OK;
952 }
953
954 HRESULT WINAPI DMUSIC_CopyDescriptor (LPDMUS_OBJECTDESC pDst, LPDMUS_OBJECTDESC pSrc) {
955         TRACE(": copy \n%s", debugstr_DMUS_OBJECTDESC(pSrc));
956         /* copy field by field */
957         if (pSrc->dwValidData & DMUS_OBJ_CLASS) pDst->guidClass = pSrc->guidClass;
958         if (pSrc->dwValidData & DMUS_OBJ_OBJECT) pDst->guidObject = pSrc->guidObject;
959         if (pSrc->dwValidData & DMUS_OBJ_DATE) pDst->ftDate = pSrc->ftDate;
960         if (pSrc->dwValidData & DMUS_OBJ_VERSION) pDst->vVersion = pSrc->vVersion;
961         if (pSrc->dwValidData & DMUS_OBJ_NAME) strcpyW (pDst->wszName, pSrc->wszName);
962         if (pSrc->dwValidData & DMUS_OBJ_CATEGORY) strcpyW (pDst->wszCategory, pSrc->wszCategory);
963         if (pSrc->dwValidData & DMUS_OBJ_FILENAME) strcpyW (pDst->wszFileName, pSrc->wszFileName);
964         if (pSrc->dwValidData & DMUS_OBJ_STREAM) IStream_Clone (pSrc->pStream, &pDst->pStream);
965         if (pSrc->dwValidData & DMUS_OBJ_MEMORY) {
966                 pDst->pbMemData = pSrc->pbMemData;
967                 pDst->llMemLength = pSrc->llMemLength;
968         }
969         /* set flags */
970         pDst->dwValidData |= pSrc->dwValidData;
971         return S_OK;
972 }
973
974 BOOL WINAPI DMUSIC_IsValidLoadableClass (REFCLSID pClassID) {
975         if (IsEqualCLSID(pClassID, &CLSID_DirectMusicAudioPathConfig) ||
976                 IsEqualCLSID(pClassID, &CLSID_DirectMusicBand) ||
977                 IsEqualCLSID(pClassID, &CLSID_DirectMusicContainer) ||
978                 IsEqualCLSID(pClassID, &CLSID_DirectMusicCollection) ||
979                 IsEqualCLSID(pClassID, &CLSID_DirectMusicChordMap) ||
980                 IsEqualCLSID(pClassID, &CLSID_DirectMusicSegment) ||
981                 IsEqualCLSID(pClassID, &CLSID_DirectMusicScript) ||
982                 IsEqualCLSID(pClassID, &CLSID_DirectMusicSong) ||
983                 IsEqualCLSID(pClassID, &CLSID_DirectMusicStyle) ||
984                 IsEqualCLSID(pClassID, &CLSID_DirectMusicGraph) ||
985                 IsEqualCLSID(pClassID, &CLSID_DirectSoundWave) ||
986                 IsEqualCLSID(pClassID, &GUID_DirectMusicAllTypes))
987                 return TRUE;
988         else
989                 return FALSE;
990 }