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