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