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