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