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